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 15 static 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 45 out: 46 mutex_unlock(&private->io_mutex); 47 return ret; 48 } 49 50 static 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 58 static void vfio_ccw_schib_region_release(struct vfio_ccw_private *private, 59 struct vfio_ccw_region *region) 60 { 61 62 } 63 64 static 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 70 int 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 80 static 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 123 static 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 130 static void vfio_ccw_crw_region_release(struct vfio_ccw_private *private, 131 struct vfio_ccw_region *region) 132 { 133 134 } 135 136 static 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 142 int 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