Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 1 | /* |
| 2 | * vfio based subchannel assignment support |
| 3 | * |
| 4 | * Copyright 2017 IBM Corp. |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 5 | * Copyright 2019 Red Hat, Inc. |
| 6 | * |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 7 | * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> |
| 8 | * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com> |
| 9 | * Pierre Morel <pmorel@linux.vnet.ibm.com> |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 10 | * Cornelia Huck <cohuck@redhat.com> |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 11 | * |
Cornelia Huck | 08b824a | 2018-02-27 18:25:41 +0100 | [diff] [blame] | 12 | * This work is licensed under the terms of the GNU GPL, version 2 or (at |
| 13 | * your option) any later version. See the COPYING file in the top-level |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 14 | * directory. |
| 15 | */ |
| 16 | |
Philippe Mathieu-Daudé | e9808d0 | 2017-10-17 13:43:53 -0300 | [diff] [blame] | 17 | #include "qemu/osdep.h" |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 18 | #include <linux/vfio.h> |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 19 | #include <linux/vfio_ccw.h> |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 20 | #include <sys/ioctl.h> |
| 21 | |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 22 | #include "qapi/error.h" |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 23 | #include "hw/vfio/vfio.h" |
| 24 | #include "hw/vfio/vfio-common.h" |
| 25 | #include "hw/s390x/s390-ccw.h" |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 26 | #include "hw/s390x/vfio-ccw.h" |
Markus Armbruster | a27bd6c | 2019-08-12 07:23:51 +0200 | [diff] [blame] | 27 | #include "hw/qdev-properties.h" |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 28 | #include "hw/s390x/ccw-device.h" |
Philippe Mathieu-Daudé | d791937 | 2018-05-28 20:26:59 -0300 | [diff] [blame] | 29 | #include "exec/address-spaces.h" |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 30 | #include "qemu/error-report.h" |
Markus Armbruster | db72581 | 2019-08-12 07:23:50 +0200 | [diff] [blame] | 31 | #include "qemu/main-loop.h" |
Markus Armbruster | 0b8fa32 | 2019-05-23 16:35:07 +0200 | [diff] [blame] | 32 | #include "qemu/module.h" |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 33 | |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 34 | struct VFIOCCWDevice { |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 35 | S390CCWDevice cdev; |
| 36 | VFIODevice vdev; |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 37 | uint64_t io_region_size; |
| 38 | uint64_t io_region_offset; |
| 39 | struct ccw_io_region *io_region; |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 40 | uint64_t async_cmd_region_size; |
| 41 | uint64_t async_cmd_region_offset; |
| 42 | struct ccw_cmd_region *async_cmd_region; |
Farhan Ali | 46ea384 | 2020-05-05 14:57:54 +0200 | [diff] [blame] | 43 | uint64_t schib_region_size; |
| 44 | uint64_t schib_region_offset; |
| 45 | struct ccw_schib_region *schib_region; |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 46 | uint64_t crw_region_size; |
| 47 | uint64_t crw_region_offset; |
| 48 | struct ccw_crw_region *crw_region; |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 49 | EventNotifier io_notifier; |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 50 | EventNotifier crw_notifier; |
Eric Farman | b2f96f9 | 2021-01-04 21:20:57 +0100 | [diff] [blame] | 51 | EventNotifier req_notifier; |
Halil Pasic | 9a51c9e | 2018-05-24 19:58:27 +0200 | [diff] [blame] | 52 | bool force_orb_pfch; |
| 53 | bool warned_orb_pfch; |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 54 | }; |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 55 | |
Halil Pasic | 9a51c9e | 2018-05-24 19:58:27 +0200 | [diff] [blame] | 56 | static inline void warn_once_pfch(VFIOCCWDevice *vcdev, SubchDev *sch, |
| 57 | const char *msg) |
| 58 | { |
Cornelia Huck | c55510b | 2018-08-30 16:59:01 +0200 | [diff] [blame] | 59 | warn_report_once_cond(&vcdev->warned_orb_pfch, |
| 60 | "vfio-ccw (devno %x.%x.%04x): %s", |
| 61 | sch->cssid, sch->ssid, sch->devno, msg); |
Halil Pasic | 9a51c9e | 2018-05-24 19:58:27 +0200 | [diff] [blame] | 62 | } |
| 63 | |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 64 | static void vfio_ccw_compute_needs_reset(VFIODevice *vdev) |
| 65 | { |
| 66 | vdev->needs_reset = false; |
| 67 | } |
| 68 | |
| 69 | /* |
| 70 | * We don't need vfio_hot_reset_multi and vfio_eoi operations for |
| 71 | * vfio_ccw device now. |
| 72 | */ |
| 73 | struct VFIODeviceOps vfio_ccw_ops = { |
| 74 | .vfio_compute_needs_reset = vfio_ccw_compute_needs_reset, |
| 75 | }; |
| 76 | |
Halil Pasic | 66dc50f | 2017-10-17 16:04:49 +0200 | [diff] [blame] | 77 | static IOInstEnding vfio_ccw_handle_request(SubchDev *sch) |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 78 | { |
Halil Pasic | 66dc50f | 2017-10-17 16:04:49 +0200 | [diff] [blame] | 79 | S390CCWDevice *cdev = sch->driver_data; |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 80 | VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); |
| 81 | struct ccw_io_region *region = vcdev->io_region; |
| 82 | int ret; |
| 83 | |
Jared Rossi | 24e58a7 | 2020-05-12 14:15:35 -0400 | [diff] [blame] | 84 | if (!(sch->orb.ctrl0 & ORB_CTRL0_MASK_PFCH) && vcdev->force_orb_pfch) { |
| 85 | sch->orb.ctrl0 |= ORB_CTRL0_MASK_PFCH; |
| 86 | warn_once_pfch(vcdev, sch, "PFCH flag forced"); |
Halil Pasic | 9a51c9e | 2018-05-24 19:58:27 +0200 | [diff] [blame] | 87 | } |
| 88 | |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 89 | QEMU_BUILD_BUG_ON(sizeof(region->orb_area) != sizeof(ORB)); |
| 90 | QEMU_BUILD_BUG_ON(sizeof(region->scsw_area) != sizeof(SCSW)); |
| 91 | QEMU_BUILD_BUG_ON(sizeof(region->irb_area) != sizeof(IRB)); |
| 92 | |
| 93 | memset(region, 0, sizeof(*region)); |
| 94 | |
Halil Pasic | 66dc50f | 2017-10-17 16:04:49 +0200 | [diff] [blame] | 95 | memcpy(region->orb_area, &sch->orb, sizeof(ORB)); |
| 96 | memcpy(region->scsw_area, &sch->curr_status.scsw, sizeof(SCSW)); |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 97 | |
| 98 | again: |
| 99 | ret = pwrite(vcdev->vdev.fd, region, |
| 100 | vcdev->io_region_size, vcdev->io_region_offset); |
| 101 | if (ret != vcdev->io_region_size) { |
| 102 | if (errno == EAGAIN) { |
| 103 | goto again; |
| 104 | } |
Boris Fiuczynski | 91f751d | 2019-11-28 15:30:14 +0100 | [diff] [blame] | 105 | error_report("vfio-ccw: write I/O region failed with errno=%d", errno); |
Eric Farman | d6cd663 | 2021-03-03 17:07:39 +0100 | [diff] [blame] | 106 | ret = errno ? -errno : -EFAULT; |
Halil Pasic | 66dc50f | 2017-10-17 16:04:49 +0200 | [diff] [blame] | 107 | } else { |
Eric Farman | d6cd663 | 2021-03-03 17:07:39 +0100 | [diff] [blame] | 108 | ret = 0; |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 109 | } |
Halil Pasic | 66dc50f | 2017-10-17 16:04:49 +0200 | [diff] [blame] | 110 | switch (ret) { |
| 111 | case 0: |
| 112 | return IOINST_CC_EXPECTED; |
| 113 | case -EBUSY: |
| 114 | return IOINST_CC_BUSY; |
| 115 | case -ENODEV: |
| 116 | case -EACCES: |
| 117 | return IOINST_CC_NOT_OPERATIONAL; |
| 118 | case -EFAULT: |
| 119 | default: |
| 120 | sch_gen_unit_exception(sch); |
| 121 | css_inject_io_interrupt(sch); |
| 122 | return IOINST_CC_EXPECTED; |
| 123 | } |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 124 | } |
| 125 | |
Farhan Ali | 46ea384 | 2020-05-05 14:57:54 +0200 | [diff] [blame] | 126 | static IOInstEnding vfio_ccw_handle_store(SubchDev *sch) |
| 127 | { |
| 128 | S390CCWDevice *cdev = sch->driver_data; |
| 129 | VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); |
| 130 | SCHIB *schib = &sch->curr_status; |
| 131 | struct ccw_schib_region *region = vcdev->schib_region; |
| 132 | SCHIB *s; |
| 133 | int ret; |
| 134 | |
| 135 | /* schib region not available so nothing else to do */ |
| 136 | if (!region) { |
| 137 | return IOINST_CC_EXPECTED; |
| 138 | } |
| 139 | |
| 140 | memset(region, 0, sizeof(*region)); |
| 141 | ret = pread(vcdev->vdev.fd, region, vcdev->schib_region_size, |
| 142 | vcdev->schib_region_offset); |
| 143 | |
| 144 | if (ret == -1) { |
| 145 | /* |
| 146 | * Device is probably damaged, but store subchannel does not |
| 147 | * have a nonzero cc defined for this scenario. Log an error, |
| 148 | * and presume things are otherwise fine. |
| 149 | */ |
| 150 | error_report("vfio-ccw: store region read failed with errno=%d", errno); |
| 151 | return IOINST_CC_EXPECTED; |
| 152 | } |
| 153 | |
| 154 | /* |
| 155 | * Selectively copy path-related bits of the SCHIB, |
| 156 | * rather than copying the entire struct. |
| 157 | */ |
| 158 | s = (SCHIB *)region->schib_area; |
| 159 | schib->pmcw.pnom = s->pmcw.pnom; |
| 160 | schib->pmcw.lpum = s->pmcw.lpum; |
| 161 | schib->pmcw.pam = s->pmcw.pam; |
| 162 | schib->pmcw.pom = s->pmcw.pom; |
| 163 | |
| 164 | if (s->scsw.flags & SCSW_FLAGS_MASK_PNO) { |
| 165 | schib->scsw.flags |= SCSW_FLAGS_MASK_PNO; |
| 166 | } |
| 167 | |
| 168 | return IOINST_CC_EXPECTED; |
| 169 | } |
| 170 | |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 171 | static int vfio_ccw_handle_clear(SubchDev *sch) |
| 172 | { |
| 173 | S390CCWDevice *cdev = sch->driver_data; |
| 174 | VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); |
| 175 | struct ccw_cmd_region *region = vcdev->async_cmd_region; |
| 176 | int ret; |
| 177 | |
| 178 | if (!vcdev->async_cmd_region) { |
| 179 | /* Async command region not available, fall back to emulation */ |
| 180 | return -ENOSYS; |
| 181 | } |
| 182 | |
| 183 | memset(region, 0, sizeof(*region)); |
| 184 | region->command = VFIO_CCW_ASYNC_CMD_CSCH; |
| 185 | |
| 186 | again: |
| 187 | ret = pwrite(vcdev->vdev.fd, region, |
| 188 | vcdev->async_cmd_region_size, vcdev->async_cmd_region_offset); |
| 189 | if (ret != vcdev->async_cmd_region_size) { |
| 190 | if (errno == EAGAIN) { |
| 191 | goto again; |
| 192 | } |
| 193 | error_report("vfio-ccw: write cmd region failed with errno=%d", errno); |
Eric Farman | d6cd663 | 2021-03-03 17:07:39 +0100 | [diff] [blame] | 194 | ret = errno ? -errno : -EFAULT; |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 195 | } else { |
Eric Farman | d6cd663 | 2021-03-03 17:07:39 +0100 | [diff] [blame] | 196 | ret = 0; |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 197 | } |
| 198 | switch (ret) { |
| 199 | case 0: |
| 200 | case -ENODEV: |
| 201 | case -EACCES: |
Cornelia Huck | 759a5d3 | 2021-07-05 18:39:51 +0200 | [diff] [blame] | 202 | return ret; |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 203 | case -EFAULT: |
| 204 | default: |
| 205 | sch_gen_unit_exception(sch); |
| 206 | css_inject_io_interrupt(sch); |
| 207 | return 0; |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | static int vfio_ccw_handle_halt(SubchDev *sch) |
| 212 | { |
| 213 | S390CCWDevice *cdev = sch->driver_data; |
| 214 | VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); |
| 215 | struct ccw_cmd_region *region = vcdev->async_cmd_region; |
| 216 | int ret; |
| 217 | |
| 218 | if (!vcdev->async_cmd_region) { |
| 219 | /* Async command region not available, fall back to emulation */ |
| 220 | return -ENOSYS; |
| 221 | } |
| 222 | |
| 223 | memset(region, 0, sizeof(*region)); |
| 224 | region->command = VFIO_CCW_ASYNC_CMD_HSCH; |
| 225 | |
| 226 | again: |
| 227 | ret = pwrite(vcdev->vdev.fd, region, |
| 228 | vcdev->async_cmd_region_size, vcdev->async_cmd_region_offset); |
| 229 | if (ret != vcdev->async_cmd_region_size) { |
| 230 | if (errno == EAGAIN) { |
| 231 | goto again; |
| 232 | } |
| 233 | error_report("vfio-ccw: write cmd region failed with errno=%d", errno); |
Eric Farman | d6cd663 | 2021-03-03 17:07:39 +0100 | [diff] [blame] | 234 | ret = errno ? -errno : -EFAULT; |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 235 | } else { |
Eric Farman | d6cd663 | 2021-03-03 17:07:39 +0100 | [diff] [blame] | 236 | ret = 0; |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 237 | } |
| 238 | switch (ret) { |
| 239 | case 0: |
| 240 | case -EBUSY: |
| 241 | case -ENODEV: |
| 242 | case -EACCES: |
Cornelia Huck | 759a5d3 | 2021-07-05 18:39:51 +0200 | [diff] [blame] | 243 | return ret; |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 244 | case -EFAULT: |
| 245 | default: |
| 246 | sch_gen_unit_exception(sch); |
| 247 | css_inject_io_interrupt(sch); |
| 248 | return 0; |
| 249 | } |
| 250 | } |
| 251 | |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 252 | static void vfio_ccw_reset(DeviceState *dev) |
| 253 | { |
| 254 | CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev); |
| 255 | S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev); |
| 256 | VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); |
| 257 | |
| 258 | ioctl(vcdev->vdev.fd, VFIO_DEVICE_RESET); |
| 259 | } |
| 260 | |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 261 | static void vfio_ccw_crw_read(VFIOCCWDevice *vcdev) |
| 262 | { |
| 263 | struct ccw_crw_region *region = vcdev->crw_region; |
| 264 | CRW crw; |
| 265 | int size; |
| 266 | |
| 267 | /* Keep reading CRWs as long as data is returned */ |
| 268 | do { |
| 269 | memset(region, 0, sizeof(*region)); |
| 270 | size = pread(vcdev->vdev.fd, region, vcdev->crw_region_size, |
| 271 | vcdev->crw_region_offset); |
| 272 | |
| 273 | if (size == -1) { |
| 274 | error_report("vfio-ccw: Read crw region failed with errno=%d", |
| 275 | errno); |
| 276 | break; |
| 277 | } |
| 278 | |
| 279 | if (region->crw == 0) { |
| 280 | /* No more CRWs to queue */ |
| 281 | break; |
| 282 | } |
| 283 | |
| 284 | memcpy(&crw, ®ion->crw, sizeof(CRW)); |
| 285 | |
| 286 | css_crw_add_to_queue(crw); |
| 287 | } while (1); |
| 288 | } |
| 289 | |
Eric Farman | b2f96f9 | 2021-01-04 21:20:57 +0100 | [diff] [blame] | 290 | static void vfio_ccw_req_notifier_handler(void *opaque) |
| 291 | { |
| 292 | VFIOCCWDevice *vcdev = opaque; |
| 293 | Error *err = NULL; |
| 294 | |
| 295 | if (!event_notifier_test_and_clear(&vcdev->req_notifier)) { |
| 296 | return; |
| 297 | } |
| 298 | |
| 299 | qdev_unplug(DEVICE(vcdev), &err); |
| 300 | if (err) { |
| 301 | warn_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name); |
| 302 | } |
| 303 | } |
| 304 | |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 305 | static void vfio_ccw_crw_notifier_handler(void *opaque) |
| 306 | { |
| 307 | VFIOCCWDevice *vcdev = opaque; |
| 308 | |
| 309 | while (event_notifier_test_and_clear(&vcdev->crw_notifier)) { |
| 310 | vfio_ccw_crw_read(vcdev); |
| 311 | } |
| 312 | } |
| 313 | |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 314 | static void vfio_ccw_io_notifier_handler(void *opaque) |
| 315 | { |
| 316 | VFIOCCWDevice *vcdev = opaque; |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 317 | struct ccw_io_region *region = vcdev->io_region; |
| 318 | S390CCWDevice *cdev = S390_CCW_DEVICE(vcdev); |
| 319 | CcwDevice *ccw_dev = CCW_DEVICE(cdev); |
| 320 | SubchDev *sch = ccw_dev->sch; |
Daniel P. Berrangé | e1d0b37 | 2019-03-29 11:11:01 +0000 | [diff] [blame] | 321 | SCHIB *schib = &sch->curr_status; |
| 322 | SCSW s; |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 323 | IRB irb; |
Eric Farman | c626710 | 2021-06-18 01:25:37 +0200 | [diff] [blame] | 324 | ESW esw; |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 325 | int size; |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 326 | |
| 327 | if (!event_notifier_test_and_clear(&vcdev->io_notifier)) { |
| 328 | return; |
| 329 | } |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 330 | |
| 331 | size = pread(vcdev->vdev.fd, region, vcdev->io_region_size, |
| 332 | vcdev->io_region_offset); |
| 333 | if (size == -1) { |
| 334 | switch (errno) { |
| 335 | case ENODEV: |
| 336 | /* Generate a deferred cc 3 condition. */ |
Daniel P. Berrangé | e1d0b37 | 2019-03-29 11:11:01 +0000 | [diff] [blame] | 337 | schib->scsw.flags |= SCSW_FLAGS_MASK_CC; |
| 338 | schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; |
| 339 | schib->scsw.ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND); |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 340 | goto read_err; |
| 341 | case EFAULT: |
| 342 | /* Memory problem, generate channel data check. */ |
Daniel P. Berrangé | e1d0b37 | 2019-03-29 11:11:01 +0000 | [diff] [blame] | 343 | schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND; |
| 344 | schib->scsw.cstat = SCSW_CSTAT_DATA_CHECK; |
| 345 | schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; |
| 346 | schib->scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 347 | SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; |
| 348 | goto read_err; |
| 349 | default: |
| 350 | /* Error, generate channel program check. */ |
Daniel P. Berrangé | e1d0b37 | 2019-03-29 11:11:01 +0000 | [diff] [blame] | 351 | schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND; |
| 352 | schib->scsw.cstat = SCSW_CSTAT_PROG_CHECK; |
| 353 | schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; |
| 354 | schib->scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 355 | SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; |
| 356 | goto read_err; |
| 357 | } |
| 358 | } else if (size != vcdev->io_region_size) { |
| 359 | /* Information transfer error, generate channel-control check. */ |
Daniel P. Berrangé | e1d0b37 | 2019-03-29 11:11:01 +0000 | [diff] [blame] | 360 | schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND; |
| 361 | schib->scsw.cstat = SCSW_CSTAT_CHN_CTRL_CHK; |
| 362 | schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; |
| 363 | schib->scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 364 | SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; |
| 365 | goto read_err; |
| 366 | } |
| 367 | |
| 368 | memcpy(&irb, region->irb_area, sizeof(IRB)); |
| 369 | |
| 370 | /* Update control block via irb. */ |
Daniel P. Berrangé | e1d0b37 | 2019-03-29 11:11:01 +0000 | [diff] [blame] | 371 | s = schib->scsw; |
| 372 | copy_scsw_to_guest(&s, &irb.scsw); |
| 373 | schib->scsw = s; |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 374 | |
Eric Farman | c626710 | 2021-06-18 01:25:37 +0200 | [diff] [blame] | 375 | copy_esw_to_guest(&esw, &irb.esw); |
| 376 | sch->esw = esw; |
| 377 | |
Dong Jia Shi | 334e768 | 2017-05-17 02:48:12 +0200 | [diff] [blame] | 378 | /* If a uint check is pending, copy sense data. */ |
Daniel P. Berrangé | e1d0b37 | 2019-03-29 11:11:01 +0000 | [diff] [blame] | 379 | if ((schib->scsw.dstat & SCSW_DSTAT_UNIT_CHECK) && |
| 380 | (schib->pmcw.chars & PMCW_CHARS_MASK_CSENSE)) { |
Dong Jia Shi | 334e768 | 2017-05-17 02:48:12 +0200 | [diff] [blame] | 381 | memcpy(sch->sense_data, irb.ecw, sizeof(irb.ecw)); |
| 382 | } |
| 383 | |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 384 | read_err: |
| 385 | css_inject_io_interrupt(sch); |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 386 | } |
| 387 | |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 388 | static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, |
| 389 | unsigned int irq, |
| 390 | Error **errp) |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 391 | { |
| 392 | VFIODevice *vdev = &vcdev->vdev; |
| 393 | struct vfio_irq_info *irq_info; |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 394 | size_t argsz; |
Cornelia Huck | b5e89f0 | 2019-04-12 13:42:31 +0200 | [diff] [blame] | 395 | int fd; |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 396 | EventNotifier *notifier; |
| 397 | IOHandler *fd_read; |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 398 | |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 399 | switch (irq) { |
| 400 | case VFIO_CCW_IO_IRQ_INDEX: |
| 401 | notifier = &vcdev->io_notifier; |
| 402 | fd_read = vfio_ccw_io_notifier_handler; |
| 403 | break; |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 404 | case VFIO_CCW_CRW_IRQ_INDEX: |
| 405 | notifier = &vcdev->crw_notifier; |
| 406 | fd_read = vfio_ccw_crw_notifier_handler; |
| 407 | break; |
Eric Farman | b2f96f9 | 2021-01-04 21:20:57 +0100 | [diff] [blame] | 408 | case VFIO_CCW_REQ_IRQ_INDEX: |
| 409 | notifier = &vcdev->req_notifier; |
| 410 | fd_read = vfio_ccw_req_notifier_handler; |
| 411 | break; |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 412 | default: |
| 413 | error_setg(errp, "vfio: Unsupported device irq(%d)", irq); |
| 414 | return; |
| 415 | } |
| 416 | |
| 417 | if (vdev->num_irqs < irq + 1) { |
Eric Farman | 6178d46 | 2021-04-21 17:20:53 +0200 | [diff] [blame] | 418 | error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)", |
| 419 | irq, vdev->num_irqs); |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 420 | return; |
| 421 | } |
| 422 | |
Jing Zhang | 28e22d4 | 2017-07-18 03:49:25 +0200 | [diff] [blame] | 423 | argsz = sizeof(*irq_info); |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 424 | irq_info = g_malloc0(argsz); |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 425 | irq_info->index = irq; |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 426 | irq_info->argsz = argsz; |
| 427 | if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, |
| 428 | irq_info) < 0 || irq_info->count < 1) { |
| 429 | error_setg_errno(errp, errno, "vfio: Error getting irq info"); |
| 430 | goto out_free_info; |
| 431 | } |
| 432 | |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 433 | if (event_notifier_init(notifier, 0)) { |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 434 | error_setg_errno(errp, errno, |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 435 | "vfio: Unable to init event notifier for irq (%d)", |
| 436 | irq); |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 437 | goto out_free_info; |
| 438 | } |
| 439 | |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 440 | fd = event_notifier_get_fd(notifier); |
| 441 | qemu_set_fd_handler(fd, fd_read, NULL, vcdev); |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 442 | |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 443 | if (vfio_set_irq_signaling(vdev, irq, 0, |
Cornelia Huck | b5e89f0 | 2019-04-12 13:42:31 +0200 | [diff] [blame] | 444 | VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { |
| 445 | qemu_set_fd_handler(fd, NULL, NULL, vcdev); |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 446 | event_notifier_cleanup(notifier); |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 447 | } |
| 448 | |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 449 | out_free_info: |
| 450 | g_free(irq_info); |
| 451 | } |
| 452 | |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 453 | static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, |
| 454 | unsigned int irq) |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 455 | { |
Cornelia Huck | b5e89f0 | 2019-04-12 13:42:31 +0200 | [diff] [blame] | 456 | Error *err = NULL; |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 457 | EventNotifier *notifier; |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 458 | |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 459 | switch (irq) { |
| 460 | case VFIO_CCW_IO_IRQ_INDEX: |
| 461 | notifier = &vcdev->io_notifier; |
| 462 | break; |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 463 | case VFIO_CCW_CRW_IRQ_INDEX: |
| 464 | notifier = &vcdev->crw_notifier; |
| 465 | break; |
Eric Farman | b2f96f9 | 2021-01-04 21:20:57 +0100 | [diff] [blame] | 466 | case VFIO_CCW_REQ_IRQ_INDEX: |
| 467 | notifier = &vcdev->req_notifier; |
| 468 | break; |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 469 | default: |
| 470 | error_report("vfio: Unsupported device irq(%d)", irq); |
| 471 | return; |
| 472 | } |
| 473 | |
| 474 | if (vfio_set_irq_signaling(&vcdev->vdev, irq, 0, |
Alex Williamson | f5cf94c | 2019-07-02 13:41:34 -0600 | [diff] [blame] | 475 | VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { |
Eric Farman | dcc9cf3 | 2021-04-28 16:36:52 +0200 | [diff] [blame] | 476 | warn_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name); |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 477 | } |
| 478 | |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 479 | qemu_set_fd_handler(event_notifier_get_fd(notifier), |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 480 | NULL, NULL, vcdev); |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 481 | event_notifier_cleanup(notifier); |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 482 | } |
| 483 | |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 484 | static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) |
| 485 | { |
| 486 | VFIODevice *vdev = &vcdev->vdev; |
| 487 | struct vfio_region_info *info; |
| 488 | int ret; |
| 489 | |
| 490 | /* Sanity check device */ |
| 491 | if (!(vdev->flags & VFIO_DEVICE_FLAGS_CCW)) { |
| 492 | error_setg(errp, "vfio: Um, this isn't a vfio-ccw device"); |
| 493 | return; |
| 494 | } |
| 495 | |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 496 | /* |
| 497 | * We always expect at least the I/O region to be present. We also |
| 498 | * may have a variable number of regions governed by capabilities. |
| 499 | */ |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 500 | if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) { |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 501 | error_setg(errp, "vfio: too few regions (%u), expected at least %u", |
| 502 | vdev->num_regions, VFIO_CCW_CONFIG_REGION_INDEX + 1); |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 503 | return; |
| 504 | } |
| 505 | |
| 506 | ret = vfio_get_region_info(vdev, VFIO_CCW_CONFIG_REGION_INDEX, &info); |
| 507 | if (ret) { |
| 508 | error_setg_errno(errp, -ret, "vfio: Error getting config info"); |
| 509 | return; |
| 510 | } |
| 511 | |
| 512 | vcdev->io_region_size = info->size; |
| 513 | if (sizeof(*vcdev->io_region) != vcdev->io_region_size) { |
| 514 | error_setg(errp, "vfio: Unexpected size of the I/O region"); |
Eric Farman | 2a3b9cb | 2020-05-05 14:57:53 +0200 | [diff] [blame] | 515 | goto out_err; |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 516 | } |
| 517 | |
| 518 | vcdev->io_region_offset = info->offset; |
| 519 | vcdev->io_region = g_malloc0(info->size); |
Cornelia Huck | c8726f7 | 2020-09-28 12:17:01 +0200 | [diff] [blame] | 520 | g_free(info); |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 521 | |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 522 | /* check for the optional async command region */ |
| 523 | ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, |
| 524 | VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD, &info); |
| 525 | if (!ret) { |
| 526 | vcdev->async_cmd_region_size = info->size; |
| 527 | if (sizeof(*vcdev->async_cmd_region) != vcdev->async_cmd_region_size) { |
| 528 | error_setg(errp, "vfio: Unexpected size of the async cmd region"); |
Eric Farman | 2a3b9cb | 2020-05-05 14:57:53 +0200 | [diff] [blame] | 529 | goto out_err; |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 530 | } |
| 531 | vcdev->async_cmd_region_offset = info->offset; |
| 532 | vcdev->async_cmd_region = g_malloc0(info->size); |
Cornelia Huck | c8726f7 | 2020-09-28 12:17:01 +0200 | [diff] [blame] | 533 | g_free(info); |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 534 | } |
| 535 | |
Farhan Ali | 46ea384 | 2020-05-05 14:57:54 +0200 | [diff] [blame] | 536 | ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, |
| 537 | VFIO_REGION_SUBTYPE_CCW_SCHIB, &info); |
| 538 | if (!ret) { |
| 539 | vcdev->schib_region_size = info->size; |
| 540 | if (sizeof(*vcdev->schib_region) != vcdev->schib_region_size) { |
| 541 | error_setg(errp, "vfio: Unexpected size of the schib region"); |
| 542 | goto out_err; |
| 543 | } |
| 544 | vcdev->schib_region_offset = info->offset; |
| 545 | vcdev->schib_region = g_malloc(info->size); |
Cornelia Huck | c8726f7 | 2020-09-28 12:17:01 +0200 | [diff] [blame] | 546 | g_free(info); |
Farhan Ali | 46ea384 | 2020-05-05 14:57:54 +0200 | [diff] [blame] | 547 | } |
| 548 | |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 549 | ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, |
| 550 | VFIO_REGION_SUBTYPE_CCW_CRW, &info); |
| 551 | |
| 552 | if (!ret) { |
| 553 | vcdev->crw_region_size = info->size; |
| 554 | if (sizeof(*vcdev->crw_region) != vcdev->crw_region_size) { |
| 555 | error_setg(errp, "vfio: Unexpected size of the CRW region"); |
| 556 | goto out_err; |
| 557 | } |
| 558 | vcdev->crw_region_offset = info->offset; |
| 559 | vcdev->crw_region = g_malloc(info->size); |
Cornelia Huck | c8726f7 | 2020-09-28 12:17:01 +0200 | [diff] [blame] | 560 | g_free(info); |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 561 | } |
| 562 | |
Eric Farman | 2a3b9cb | 2020-05-05 14:57:53 +0200 | [diff] [blame] | 563 | return; |
| 564 | |
| 565 | out_err: |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 566 | g_free(vcdev->crw_region); |
Farhan Ali | 46ea384 | 2020-05-05 14:57:54 +0200 | [diff] [blame] | 567 | g_free(vcdev->schib_region); |
Eric Farman | 2a3b9cb | 2020-05-05 14:57:53 +0200 | [diff] [blame] | 568 | g_free(vcdev->async_cmd_region); |
| 569 | g_free(vcdev->io_region); |
| 570 | g_free(info); |
| 571 | return; |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 572 | } |
| 573 | |
| 574 | static void vfio_ccw_put_region(VFIOCCWDevice *vcdev) |
| 575 | { |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 576 | g_free(vcdev->crw_region); |
Farhan Ali | 46ea384 | 2020-05-05 14:57:54 +0200 | [diff] [blame] | 577 | g_free(vcdev->schib_region); |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 578 | g_free(vcdev->async_cmd_region); |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 579 | g_free(vcdev->io_region); |
| 580 | } |
| 581 | |
Greg Kurz | c96f2c2 | 2018-04-09 12:15:10 +0200 | [diff] [blame] | 582 | static void vfio_ccw_put_device(VFIOCCWDevice *vcdev) |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 583 | { |
| 584 | g_free(vcdev->vdev.name); |
| 585 | vfio_put_base_device(&vcdev->vdev); |
| 586 | } |
| 587 | |
Greg Kurz | c96f2c2 | 2018-04-09 12:15:10 +0200 | [diff] [blame] | 588 | static void vfio_ccw_get_device(VFIOGroup *group, VFIOCCWDevice *vcdev, |
| 589 | Error **errp) |
| 590 | { |
| 591 | char *name = g_strdup_printf("%x.%x.%04x", vcdev->cdev.hostid.cssid, |
| 592 | vcdev->cdev.hostid.ssid, |
| 593 | vcdev->cdev.hostid.devid); |
| 594 | VFIODevice *vbasedev; |
| 595 | |
| 596 | QLIST_FOREACH(vbasedev, &group->device_list, next) { |
| 597 | if (strcmp(vbasedev->name, name) == 0) { |
| 598 | error_setg(errp, "vfio: subchannel %s has already been attached", |
| 599 | name); |
| 600 | goto out_err; |
| 601 | } |
| 602 | } |
| 603 | |
Alex Williamson | 238e917 | 2018-08-17 09:27:16 -0600 | [diff] [blame] | 604 | /* |
| 605 | * All vfio-ccw devices are believed to operate in a way compatible with |
David Hildenbrand | aff92b8 | 2020-06-26 09:22:30 +0200 | [diff] [blame] | 606 | * discarding of memory in RAM blocks, ie. pages pinned in the host are |
| 607 | * in the current working set of the guest driver and therefore never |
| 608 | * overlap e.g., with pages available to the guest balloon driver. This |
| 609 | * needs to be set before vfio_get_device() for vfio common to handle |
| 610 | * ram_block_discard_disable(). |
Alex Williamson | 238e917 | 2018-08-17 09:27:16 -0600 | [diff] [blame] | 611 | */ |
David Hildenbrand | aff92b8 | 2020-06-26 09:22:30 +0200 | [diff] [blame] | 612 | vcdev->vdev.ram_block_discard_allowed = true; |
Alex Williamson | 238e917 | 2018-08-17 09:27:16 -0600 | [diff] [blame] | 613 | |
Greg Kurz | c96f2c2 | 2018-04-09 12:15:10 +0200 | [diff] [blame] | 614 | if (vfio_get_device(group, vcdev->cdev.mdevid, &vcdev->vdev, errp)) { |
| 615 | goto out_err; |
| 616 | } |
| 617 | |
| 618 | vcdev->vdev.ops = &vfio_ccw_ops; |
| 619 | vcdev->vdev.type = VFIO_DEVICE_TYPE_CCW; |
| 620 | vcdev->vdev.name = name; |
| 621 | vcdev->vdev.dev = &vcdev->cdev.parent_obj.parent_obj; |
| 622 | |
| 623 | return; |
| 624 | |
| 625 | out_err: |
| 626 | g_free(name); |
| 627 | } |
| 628 | |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 629 | static VFIOGroup *vfio_ccw_get_group(S390CCWDevice *cdev, Error **errp) |
| 630 | { |
| 631 | char *tmp, group_path[PATH_MAX]; |
| 632 | ssize_t len; |
| 633 | int groupid; |
| 634 | |
| 635 | tmp = g_strdup_printf("/sys/bus/css/devices/%x.%x.%04x/%s/iommu_group", |
| 636 | cdev->hostid.cssid, cdev->hostid.ssid, |
| 637 | cdev->hostid.devid, cdev->mdevid); |
| 638 | len = readlink(tmp, group_path, sizeof(group_path)); |
| 639 | g_free(tmp); |
| 640 | |
| 641 | if (len <= 0 || len >= sizeof(group_path)) { |
| 642 | error_setg(errp, "vfio: no iommu_group found"); |
| 643 | return NULL; |
| 644 | } |
| 645 | |
| 646 | group_path[len] = 0; |
| 647 | |
| 648 | if (sscanf(basename(group_path), "%d", &groupid) != 1) { |
| 649 | error_setg(errp, "vfio: failed to read %s", group_path); |
| 650 | return NULL; |
| 651 | } |
| 652 | |
| 653 | return vfio_get_group(groupid, &address_space_memory, errp); |
| 654 | } |
| 655 | |
| 656 | static void vfio_ccw_realize(DeviceState *dev, Error **errp) |
| 657 | { |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 658 | VFIOGroup *group; |
| 659 | CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev); |
| 660 | S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev); |
| 661 | VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); |
| 662 | S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev); |
| 663 | Error *err = NULL; |
| 664 | |
| 665 | /* Call the class init function for subchannel. */ |
| 666 | if (cdc->realize) { |
| 667 | cdc->realize(cdev, vcdev->vdev.sysfsdev, &err); |
| 668 | if (err) { |
| 669 | goto out_err_propagate; |
| 670 | } |
| 671 | } |
| 672 | |
| 673 | group = vfio_ccw_get_group(cdev, &err); |
| 674 | if (!group) { |
| 675 | goto out_group_err; |
| 676 | } |
| 677 | |
Greg Kurz | c96f2c2 | 2018-04-09 12:15:10 +0200 | [diff] [blame] | 678 | vfio_ccw_get_device(group, vcdev, &err); |
| 679 | if (err) { |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 680 | goto out_device_err; |
| 681 | } |
| 682 | |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 683 | vfio_ccw_get_region(vcdev, &err); |
| 684 | if (err) { |
| 685 | goto out_region_err; |
| 686 | } |
| 687 | |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 688 | vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, &err); |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 689 | if (err) { |
Eric Farman | b2f96f9 | 2021-01-04 21:20:57 +0100 | [diff] [blame] | 690 | goto out_io_notifier_err; |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 691 | } |
| 692 | |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 693 | if (vcdev->crw_region) { |
| 694 | vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX, &err); |
| 695 | if (err) { |
Eric Farman | dcc9cf3 | 2021-04-28 16:36:52 +0200 | [diff] [blame] | 696 | goto out_irq_notifier_err; |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 697 | } |
| 698 | } |
| 699 | |
Eric Farman | b2f96f9 | 2021-01-04 21:20:57 +0100 | [diff] [blame] | 700 | vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX, &err); |
| 701 | if (err) { |
Eric Farman | 6178d46 | 2021-04-21 17:20:53 +0200 | [diff] [blame] | 702 | /* |
| 703 | * Report this error, but do not make it a failing condition. |
| 704 | * Lack of this IRQ in the host does not prevent normal operation. |
| 705 | */ |
| 706 | error_report_err(err); |
Eric Farman | b2f96f9 | 2021-01-04 21:20:57 +0100 | [diff] [blame] | 707 | } |
| 708 | |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 709 | return; |
| 710 | |
Eric Farman | dcc9cf3 | 2021-04-28 16:36:52 +0200 | [diff] [blame] | 711 | out_irq_notifier_err: |
| 712 | vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX); |
| 713 | vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX); |
Eric Farman | b2f96f9 | 2021-01-04 21:20:57 +0100 | [diff] [blame] | 714 | vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); |
| 715 | out_io_notifier_err: |
Dong Jia Shi | 4886b3e | 2017-05-17 02:48:09 +0200 | [diff] [blame] | 716 | vfio_ccw_put_region(vcdev); |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 717 | out_region_err: |
Greg Kurz | c96f2c2 | 2018-04-09 12:15:10 +0200 | [diff] [blame] | 718 | vfio_ccw_put_device(vcdev); |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 719 | out_device_err: |
| 720 | vfio_put_group(group); |
| 721 | out_group_err: |
| 722 | if (cdc->unrealize) { |
Markus Armbruster | b69c3c2 | 2020-05-05 17:29:24 +0200 | [diff] [blame] | 723 | cdc->unrealize(cdev); |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 724 | } |
| 725 | out_err_propagate: |
| 726 | error_propagate(errp, err); |
| 727 | } |
| 728 | |
Markus Armbruster | b69c3c2 | 2020-05-05 17:29:24 +0200 | [diff] [blame] | 729 | static void vfio_ccw_unrealize(DeviceState *dev) |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 730 | { |
| 731 | CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev); |
| 732 | S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev); |
| 733 | VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); |
| 734 | S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev); |
| 735 | VFIOGroup *group = vcdev->vdev.group; |
| 736 | |
Eric Farman | b2f96f9 | 2021-01-04 21:20:57 +0100 | [diff] [blame] | 737 | vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX); |
Farhan Ali | f030532 | 2020-05-05 14:57:57 +0200 | [diff] [blame] | 738 | vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX); |
Eric Farman | 690e29b | 2020-05-05 14:57:55 +0200 | [diff] [blame] | 739 | vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); |
Dong Jia Shi | c14e706 | 2017-05-17 02:48:08 +0200 | [diff] [blame] | 740 | vfio_ccw_put_region(vcdev); |
Greg Kurz | c96f2c2 | 2018-04-09 12:15:10 +0200 | [diff] [blame] | 741 | vfio_ccw_put_device(vcdev); |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 742 | vfio_put_group(group); |
| 743 | |
| 744 | if (cdc->unrealize) { |
Markus Armbruster | b69c3c2 | 2020-05-05 17:29:24 +0200 | [diff] [blame] | 745 | cdc->unrealize(cdev); |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 746 | } |
| 747 | } |
| 748 | |
| 749 | static Property vfio_ccw_properties[] = { |
| 750 | DEFINE_PROP_STRING("sysfsdev", VFIOCCWDevice, vdev.sysfsdev), |
Halil Pasic | 9a51c9e | 2018-05-24 19:58:27 +0200 | [diff] [blame] | 751 | DEFINE_PROP_BOOL("force-orb-pfch", VFIOCCWDevice, force_orb_pfch, false), |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 752 | DEFINE_PROP_END_OF_LIST(), |
| 753 | }; |
| 754 | |
| 755 | static const VMStateDescription vfio_ccw_vmstate = { |
Li Qiang | da56e33 | 2019-05-21 08:15:41 -0700 | [diff] [blame] | 756 | .name = "vfio-ccw", |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 757 | .unmigratable = 1, |
| 758 | }; |
| 759 | |
| 760 | static void vfio_ccw_class_init(ObjectClass *klass, void *data) |
| 761 | { |
| 762 | DeviceClass *dc = DEVICE_CLASS(klass); |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 763 | S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass); |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 764 | |
Marc-André Lureau | 4f67d30 | 2020-01-10 19:30:32 +0400 | [diff] [blame] | 765 | device_class_set_props(dc, vfio_ccw_properties); |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 766 | dc->vmsd = &vfio_ccw_vmstate; |
| 767 | dc->desc = "VFIO-based subchannel assignment"; |
Cornelia Huck | bd2aef1 | 2017-10-04 10:51:49 +0200 | [diff] [blame] | 768 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 769 | dc->realize = vfio_ccw_realize; |
| 770 | dc->unrealize = vfio_ccw_unrealize; |
| 771 | dc->reset = vfio_ccw_reset; |
Xiao Feng Ren | 8ca2b37 | 2017-05-17 02:48:10 +0200 | [diff] [blame] | 772 | |
| 773 | cdc->handle_request = vfio_ccw_handle_request; |
Cornelia Huck | 8fadea2 | 2019-05-07 17:47:33 +0200 | [diff] [blame] | 774 | cdc->handle_halt = vfio_ccw_handle_halt; |
| 775 | cdc->handle_clear = vfio_ccw_handle_clear; |
Farhan Ali | 46ea384 | 2020-05-05 14:57:54 +0200 | [diff] [blame] | 776 | cdc->handle_store = vfio_ccw_handle_store; |
Xiao Feng Ren | 1dcac3e | 2017-05-17 02:48:07 +0200 | [diff] [blame] | 777 | } |
| 778 | |
| 779 | static const TypeInfo vfio_ccw_info = { |
| 780 | .name = TYPE_VFIO_CCW, |
| 781 | .parent = TYPE_S390_CCW, |
| 782 | .instance_size = sizeof(VFIOCCWDevice), |
| 783 | .class_init = vfio_ccw_class_init, |
| 784 | }; |
| 785 | |
| 786 | static void register_vfio_ccw_type(void) |
| 787 | { |
| 788 | type_register_static(&vfio_ccw_info); |
| 789 | } |
| 790 | |
| 791 | type_init(register_vfio_ccw_type) |