// SPDX-License-Identifier: GPL-2.0 /* * Channel path related status regions for vfio_ccw * * Copyright IBM Corp. 2020 * * Author(s): Farhan Ali <alifm@linux.ibm.com> * Eric Farman <farman@linux.ibm.com> */ #include <linux/slab.h> #include <linux/vfio.h> #include "vfio_ccw_private.h" static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private, char __user *buf, size_t count, loff_t *ppos) { struct subchannel *sch = to_subchannel(private->vdev.dev->parent); unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; struct ccw_schib_region *region; int ret; if (pos + count > sizeof(*region)) return -EINVAL; mutex_lock(&private->io_mutex); region = private->region[i].data; if (cio_update_schib(sch)) { ret = -ENODEV; goto out; } memcpy(region, &sch->schib, sizeof(*region)); if (copy_to_user(buf, (void *)region + pos, count)) { ret = -EFAULT; goto out; } ret = count; out: mutex_unlock(&private->io_mutex); return ret; } static ssize_t vfio_ccw_schib_region_write(struct vfio_ccw_private *private, const char __user *buf, size_t count, loff_t *ppos) { return -EINVAL; } static void vfio_ccw_schib_region_release(struct vfio_ccw_private *private, struct vfio_ccw_region *region) { } static const struct vfio_ccw_regops vfio_ccw_schib_region_ops = { .read = vfio_ccw_schib_region_read, .write = vfio_ccw_schib_region_write, .release = vfio_ccw_schib_region_release, }; int vfio_ccw_register_schib_dev_regions(struct vfio_ccw_private *private) { return vfio_ccw_register_dev_region(private, VFIO_REGION_SUBTYPE_CCW_SCHIB, &vfio_ccw_schib_region_ops, sizeof(struct ccw_schib_region), VFIO_REGION_INFO_FLAG_READ, private->schib_region); } static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private, char __user *buf, size_t count, loff_t *ppos) { unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; struct ccw_crw_region *region; struct vfio_ccw_crw *crw; int ret; if (pos + count > sizeof(*region)) return -EINVAL; crw = list_first_entry_or_null(&private->crw, struct vfio_ccw_crw, next); if (crw) list_del(&crw->next); mutex_lock(&private->io_mutex); region = private->region[i].data; if (crw) memcpy(®ion->crw, &crw->crw, sizeof(region->crw)); if (copy_to_user(buf, (void *)region + pos, count)) ret = -EFAULT; else ret = count; region->crw = 0; mutex_unlock(&private->io_mutex); kfree(crw); /* Notify the guest if more CRWs are on our queue */ if (!list_empty(&private->crw) && private->crw_trigger) eventfd_signal(private->crw_trigger, 1); return ret; } static ssize_t vfio_ccw_crw_region_write(struct vfio_ccw_private *private, const char __user *buf, size_t count, loff_t *ppos) { return -EINVAL; } static void vfio_ccw_crw_region_release(struct vfio_ccw_private *private, struct vfio_ccw_region *region) { } static const struct vfio_ccw_regops vfio_ccw_crw_region_ops = { .read = vfio_ccw_crw_region_read, .write = vfio_ccw_crw_region_write, .release = vfio_ccw_crw_region_release, }; int vfio_ccw_register_crw_dev_regions(struct vfio_ccw_private *private) { return vfio_ccw_register_dev_region(private, VFIO_REGION_SUBTYPE_CCW_CRW, &vfio_ccw_crw_region_ops, sizeof(struct ccw_crw_region), VFIO_REGION_INFO_FLAG_READ, private->crw_region); }