1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Channel path related status regions for vfio_ccw 4 * 5 * Copyright IBM Corp. 2020 6 * 7 * Author(s): Farhan Ali <alifm@linux.ibm.com> 8 * Eric Farman <farman@linux.ibm.com> 9 */ 10 11#include <linux/slab.h> 12#include <linux/vfio.h> 13#include "vfio_ccw_private.h" 14 15static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private, 16 char __user *buf, size_t count, 17 loff_t *ppos) 18{ 19 struct subchannel *sch = to_subchannel(private->vdev.dev->parent); 20 unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; 21 loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; 22 struct ccw_schib_region *region; 23 int ret; 24 25 if (pos + count > sizeof(*region)) 26 return -EINVAL; 27 28 mutex_lock(&private->io_mutex); 29 region = private->region[i].data; 30 31 if (cio_update_schib(sch)) { 32 ret = -ENODEV; 33 goto out; 34 } 35 36 memcpy(region, &sch->schib, sizeof(*region)); 37 38 if (copy_to_user(buf, (void *)region + pos, count)) { 39 ret = -EFAULT; 40 goto out; 41 } 42 43 ret = count; 44 45out: 46 mutex_unlock(&private->io_mutex); 47 return ret; 48} 49 50static ssize_t vfio_ccw_schib_region_write(struct vfio_ccw_private *private, 51 const char __user *buf, size_t count, 52 loff_t *ppos) 53{ 54 return -EINVAL; 55} 56 57 58static void vfio_ccw_schib_region_release(struct vfio_ccw_private *private, 59 struct vfio_ccw_region *region) 60{ 61 62} 63 64static const struct vfio_ccw_regops vfio_ccw_schib_region_ops = { 65 .read = vfio_ccw_schib_region_read, 66 .write = vfio_ccw_schib_region_write, 67 .release = vfio_ccw_schib_region_release, 68}; 69 70int vfio_ccw_register_schib_dev_regions(struct vfio_ccw_private *private) 71{ 72 return vfio_ccw_register_dev_region(private, 73 VFIO_REGION_SUBTYPE_CCW_SCHIB, 74 &vfio_ccw_schib_region_ops, 75 sizeof(struct ccw_schib_region), 76 VFIO_REGION_INFO_FLAG_READ, 77 private->schib_region); 78} 79 80static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private, 81 char __user *buf, size_t count, 82 loff_t *ppos) 83{ 84 unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; 85 loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; 86 struct ccw_crw_region *region; 87 struct vfio_ccw_crw *crw; 88 int ret; 89 90 if (pos + count > sizeof(*region)) 91 return -EINVAL; 92 93 crw = list_first_entry_or_null(&private->crw, 94 struct vfio_ccw_crw, next); 95 96 if (crw) 97 list_del(&crw->next); 98 99 mutex_lock(&private->io_mutex); 100 region = private->region[i].data; 101 102 if (crw) 103 memcpy(®ion->crw, &crw->crw, sizeof(region->crw)); 104 105 if (copy_to_user(buf, (void *)region + pos, count)) 106 ret = -EFAULT; 107 else 108 ret = count; 109 110 region->crw = 0; 111 112 mutex_unlock(&private->io_mutex); 113 114 kfree(crw); 115 116 /* Notify the guest if more CRWs are on our queue */ 117 if (!list_empty(&private->crw) && private->crw_trigger) 118 eventfd_signal(private->crw_trigger); 119 120 return ret; 121} 122 123static ssize_t vfio_ccw_crw_region_write(struct vfio_ccw_private *private, 124 const char __user *buf, size_t count, 125 loff_t *ppos) 126{ 127 return -EINVAL; 128} 129 130static void vfio_ccw_crw_region_release(struct vfio_ccw_private *private, 131 struct vfio_ccw_region *region) 132{ 133 134} 135 136static const struct vfio_ccw_regops vfio_ccw_crw_region_ops = { 137 .read = vfio_ccw_crw_region_read, 138 .write = vfio_ccw_crw_region_write, 139 .release = vfio_ccw_crw_region_release, 140}; 141 142int vfio_ccw_register_crw_dev_regions(struct vfio_ccw_private *private) 143{ 144 return vfio_ccw_register_dev_region(private, 145 VFIO_REGION_SUBTYPE_CCW_CRW, 146 &vfio_ccw_crw_region_ops, 147 sizeof(struct ccw_crw_region), 148 VFIO_REGION_INFO_FLAG_READ, 149 private->crw_region); 150} 151