1a5cf2bb4SCornelia Huck /* 2a5cf2bb4SCornelia Huck * virtio ccw target implementation 3a5cf2bb4SCornelia Huck * 4de6a9218SPierre Morel * Copyright 2012,2015 IBM Corp. 5a5cf2bb4SCornelia Huck * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> 6de6a9218SPierre Morel * Pierre Morel <pmorel@linux.vnet.ibm.com> 7a5cf2bb4SCornelia Huck * 8a5cf2bb4SCornelia Huck * This work is licensed under the terms of the GNU GPL, version 2 or (at 9a5cf2bb4SCornelia Huck * your option) any later version. See the COPYING file in the top-level 10a5cf2bb4SCornelia Huck * directory. 11a5cf2bb4SCornelia Huck */ 12a5cf2bb4SCornelia Huck 139615495aSPeter Maydell #include "qemu/osdep.h" 14da34e65cSMarkus Armbruster #include "qapi/error.h" 15a43de798SPaolo Bonzini #include "exec/address-spaces.h" 16bd3f16acSPaolo Bonzini #include "sysemu/kvm.h" 17a5cf2bb4SCornelia Huck #include "net/net.h" 180d09e41aSPaolo Bonzini #include "hw/virtio/virtio.h" 19ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h" 200d09e41aSPaolo Bonzini #include "hw/virtio/virtio-net.h" 21a5cf2bb4SCornelia Huck #include "qemu/bitops.h" 22d49b6836SMarkus Armbruster #include "qemu/error-report.h" 23a43de798SPaolo Bonzini #include "qemu/log.h" 240b8fa32fSMarkus Armbruster #include "qemu/module.h" 25c42767f2SThomas Huth #include "hw/virtio/virtio-access.h" 260d09e41aSPaolo Bonzini #include "hw/virtio/virtio-bus.h" 27d426d9fbSCornelia Huck #include "hw/s390x/adapter.h" 28d426d9fbSCornelia Huck #include "hw/s390x/s390_flic.h" 29a5cf2bb4SCornelia Huck 30bd3f16acSPaolo Bonzini #include "hw/s390x/ioinst.h" 31bd3f16acSPaolo Bonzini #include "hw/s390x/css.h" 32a5cf2bb4SCornelia Huck #include "virtio-ccw.h" 33a5cf2bb4SCornelia Huck #include "trace.h" 34dd70bd0dSJing Liu #include "hw/s390x/css-bridge.h" 35517ff12cSHalil Pasic #include "hw/s390x/s390-virtio-ccw.h" 363909c079SPavel Dovgalyuk #include "sysemu/replay.h" 37a5cf2bb4SCornelia Huck 38797b6086SHalil Pasic #define NR_CLASSIC_INDICATOR_BITS 64 39797b6086SHalil Pasic 402dd9d8cfSGerd Hoffmann bool have_virtio_ccw = true; 412dd9d8cfSGerd Hoffmann 42517ff12cSHalil Pasic static int virtio_ccw_dev_post_load(void *opaque, int version_id) 43517ff12cSHalil Pasic { 44517ff12cSHalil Pasic VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(opaque); 45517ff12cSHalil Pasic CcwDevice *ccw_dev = CCW_DEVICE(dev); 46517ff12cSHalil Pasic CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); 47517ff12cSHalil Pasic 48517ff12cSHalil Pasic ccw_dev->sch->driver_data = dev; 49517ff12cSHalil Pasic if (ccw_dev->sch->thinint_active) { 50517ff12cSHalil Pasic dev->routes.adapter.adapter_id = css_get_adapter_id( 51517ff12cSHalil Pasic CSS_IO_ADAPTER_VIRTIO, 52517ff12cSHalil Pasic dev->thinint_isc); 53517ff12cSHalil Pasic } 54517ff12cSHalil Pasic /* Re-fill subch_id after loading the subchannel states.*/ 55517ff12cSHalil Pasic if (ck->refill_ids) { 56517ff12cSHalil Pasic ck->refill_ids(ccw_dev); 57517ff12cSHalil Pasic } 58517ff12cSHalil Pasic return 0; 59517ff12cSHalil Pasic } 60517ff12cSHalil Pasic 61517ff12cSHalil Pasic typedef struct VirtioCcwDeviceTmp { 62517ff12cSHalil Pasic VirtioCcwDevice *parent; 63517ff12cSHalil Pasic uint16_t config_vector; 64517ff12cSHalil Pasic } VirtioCcwDeviceTmp; 65517ff12cSHalil Pasic 6644b1ff31SDr. David Alan Gilbert static int virtio_ccw_dev_tmp_pre_save(void *opaque) 67517ff12cSHalil Pasic { 68517ff12cSHalil Pasic VirtioCcwDeviceTmp *tmp = opaque; 69517ff12cSHalil Pasic VirtioCcwDevice *dev = tmp->parent; 70517ff12cSHalil Pasic VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 71517ff12cSHalil Pasic 72517ff12cSHalil Pasic tmp->config_vector = vdev->config_vector; 7344b1ff31SDr. David Alan Gilbert 7444b1ff31SDr. David Alan Gilbert return 0; 75517ff12cSHalil Pasic } 76517ff12cSHalil Pasic 77517ff12cSHalil Pasic static int virtio_ccw_dev_tmp_post_load(void *opaque, int version_id) 78517ff12cSHalil Pasic { 79517ff12cSHalil Pasic VirtioCcwDeviceTmp *tmp = opaque; 80517ff12cSHalil Pasic VirtioCcwDevice *dev = tmp->parent; 81517ff12cSHalil Pasic VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 82517ff12cSHalil Pasic 83517ff12cSHalil Pasic vdev->config_vector = tmp->config_vector; 84517ff12cSHalil Pasic return 0; 85517ff12cSHalil Pasic } 86517ff12cSHalil Pasic 87517ff12cSHalil Pasic const VMStateDescription vmstate_virtio_ccw_dev_tmp = { 88517ff12cSHalil Pasic .name = "s390_virtio_ccw_dev_tmp", 89517ff12cSHalil Pasic .pre_save = virtio_ccw_dev_tmp_pre_save, 90517ff12cSHalil Pasic .post_load = virtio_ccw_dev_tmp_post_load, 91517ff12cSHalil Pasic .fields = (VMStateField[]) { 92517ff12cSHalil Pasic VMSTATE_UINT16(config_vector, VirtioCcwDeviceTmp), 93517ff12cSHalil Pasic VMSTATE_END_OF_LIST() 94517ff12cSHalil Pasic } 95517ff12cSHalil Pasic }; 96517ff12cSHalil Pasic 97517ff12cSHalil Pasic const VMStateDescription vmstate_virtio_ccw_dev = { 98517ff12cSHalil Pasic .name = "s390_virtio_ccw_dev", 99517ff12cSHalil Pasic .version_id = 1, 100517ff12cSHalil Pasic .minimum_version_id = 1, 101517ff12cSHalil Pasic .post_load = virtio_ccw_dev_post_load, 102517ff12cSHalil Pasic .fields = (VMStateField[]) { 103517ff12cSHalil Pasic VMSTATE_CCW_DEVICE(parent_obj, VirtioCcwDevice), 104517ff12cSHalil Pasic VMSTATE_PTR_TO_IND_ADDR(indicators, VirtioCcwDevice), 105517ff12cSHalil Pasic VMSTATE_PTR_TO_IND_ADDR(indicators2, VirtioCcwDevice), 106517ff12cSHalil Pasic VMSTATE_PTR_TO_IND_ADDR(summary_indicator, VirtioCcwDevice), 107517ff12cSHalil Pasic /* 108517ff12cSHalil Pasic * Ugly hack because VirtIODevice does not migrate itself. 109517ff12cSHalil Pasic * This also makes legacy via vmstate_save_state possible. 110517ff12cSHalil Pasic */ 111517ff12cSHalil Pasic VMSTATE_WITH_TMP(VirtioCcwDevice, VirtioCcwDeviceTmp, 112517ff12cSHalil Pasic vmstate_virtio_ccw_dev_tmp), 113517ff12cSHalil Pasic VMSTATE_STRUCT(routes, VirtioCcwDevice, 1, vmstate_adapter_routes, 114517ff12cSHalil Pasic AdapterRoutes), 115517ff12cSHalil Pasic VMSTATE_UINT8(thinint_isc, VirtioCcwDevice), 116517ff12cSHalil Pasic VMSTATE_INT32(revision, VirtioCcwDevice), 117517ff12cSHalil Pasic VMSTATE_END_OF_LIST() 118517ff12cSHalil Pasic } 119517ff12cSHalil Pasic }; 120517ff12cSHalil Pasic 1211bf4d7aaSAndreas Färber static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, 1221bf4d7aaSAndreas Färber VirtioCcwDevice *dev); 123d51fcfacSKONRAD Frederic 124a5cf2bb4SCornelia Huck VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) 125a5cf2bb4SCornelia Huck { 126a5cf2bb4SCornelia Huck VirtIODevice *vdev = NULL; 127f24a6840SPaolo Bonzini VirtioCcwDevice *dev = sch->driver_data; 128a5cf2bb4SCornelia Huck 129f24a6840SPaolo Bonzini if (dev) { 130f24a6840SPaolo Bonzini vdev = virtio_bus_get_device(&dev->bus); 131a5cf2bb4SCornelia Huck } 132a5cf2bb4SCornelia Huck return vdev; 133a5cf2bb4SCornelia Huck } 134a5cf2bb4SCornelia Huck 135b4436a0bSCornelia Huck static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev) 136b4436a0bSCornelia Huck { 1377c55f68aSCornelia Huck virtio_bus_start_ioeventfd(&dev->bus); 138b4436a0bSCornelia Huck } 139b4436a0bSCornelia Huck 140b4436a0bSCornelia Huck static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev) 141b4436a0bSCornelia Huck { 1427c55f68aSCornelia Huck virtio_bus_stop_ioeventfd(&dev->bus); 1437c55f68aSCornelia Huck } 144b4436a0bSCornelia Huck 1458e93cef1SPaolo Bonzini static bool virtio_ccw_ioeventfd_enabled(DeviceState *d) 1467c55f68aSCornelia Huck { 1477c55f68aSCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1487c55f68aSCornelia Huck 1498e93cef1SPaolo Bonzini return (dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) != 0; 1507c55f68aSCornelia Huck } 1517c55f68aSCornelia Huck 1527c55f68aSCornelia Huck static int virtio_ccw_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, 1537c55f68aSCornelia Huck int n, bool assign) 1547c55f68aSCornelia Huck { 1557c55f68aSCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 156b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev); 157b804e8a6SJing Liu SubchDev *sch = ccw_dev->sch; 1587c55f68aSCornelia Huck uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid; 1597c55f68aSCornelia Huck 1607c55f68aSCornelia Huck return s390_assign_subch_ioeventfd(notifier, sch_id, n, assign); 161b4436a0bSCornelia Huck } 162b4436a0bSCornelia Huck 163a5cf2bb4SCornelia Huck /* Communication blocks used by several channel commands. */ 1640db87e0dSCornelia Huck typedef struct VqInfoBlockLegacy { 165a5cf2bb4SCornelia Huck uint64_t queue; 166a5cf2bb4SCornelia Huck uint32_t align; 167a5cf2bb4SCornelia Huck uint16_t index; 168a5cf2bb4SCornelia Huck uint16_t num; 1690db87e0dSCornelia Huck } QEMU_PACKED VqInfoBlockLegacy; 1700db87e0dSCornelia Huck 1710db87e0dSCornelia Huck typedef struct VqInfoBlock { 1720db87e0dSCornelia Huck uint64_t desc; 1730db87e0dSCornelia Huck uint32_t res0; 1740db87e0dSCornelia Huck uint16_t index; 1750db87e0dSCornelia Huck uint16_t num; 1760db87e0dSCornelia Huck uint64_t avail; 1770db87e0dSCornelia Huck uint64_t used; 178a5cf2bb4SCornelia Huck } QEMU_PACKED VqInfoBlock; 179a5cf2bb4SCornelia Huck 180a5cf2bb4SCornelia Huck typedef struct VqConfigBlock { 181a5cf2bb4SCornelia Huck uint16_t index; 182a5cf2bb4SCornelia Huck uint16_t num_max; 183a5cf2bb4SCornelia Huck } QEMU_PACKED VqConfigBlock; 184a5cf2bb4SCornelia Huck 185a5cf2bb4SCornelia Huck typedef struct VirtioFeatDesc { 186a5cf2bb4SCornelia Huck uint32_t features; 187a5cf2bb4SCornelia Huck uint8_t index; 188a5cf2bb4SCornelia Huck } QEMU_PACKED VirtioFeatDesc; 189a5cf2bb4SCornelia Huck 1907e749462SCornelia Huck typedef struct VirtioThinintInfo { 1917e749462SCornelia Huck hwaddr summary_indicator; 1927e749462SCornelia Huck hwaddr device_indicator; 1937e749462SCornelia Huck uint64_t ind_bit; 1947e749462SCornelia Huck uint8_t isc; 1957e749462SCornelia Huck } QEMU_PACKED VirtioThinintInfo; 1967e749462SCornelia Huck 197c42767f2SThomas Huth typedef struct VirtioRevInfo { 198c42767f2SThomas Huth uint16_t revision; 199c42767f2SThomas Huth uint16_t length; 200880a7817SPhilippe Mathieu-Daudé uint8_t data[]; 201c42767f2SThomas Huth } QEMU_PACKED VirtioRevInfo; 202c42767f2SThomas Huth 203a5cf2bb4SCornelia Huck /* Specify where the virtqueues for the subchannel are in guest memory. */ 2040db87e0dSCornelia Huck static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, 2050db87e0dSCornelia Huck VqInfoBlockLegacy *linfo) 206a5cf2bb4SCornelia Huck { 207f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_ccw_get_vdev(sch); 2080db87e0dSCornelia Huck uint16_t index = info ? info->index : linfo->index; 2090db87e0dSCornelia Huck uint16_t num = info ? info->num : linfo->num; 2100db87e0dSCornelia Huck uint64_t desc = info ? info->desc : linfo->queue; 211a5cf2bb4SCornelia Huck 212b1914b82SHalil Pasic if (index >= VIRTIO_QUEUE_MAX) { 213a5cf2bb4SCornelia Huck return -EINVAL; 214a5cf2bb4SCornelia Huck } 215a5cf2bb4SCornelia Huck 216a5cf2bb4SCornelia Huck /* Current code in virtio.c relies on 4K alignment. */ 2170db87e0dSCornelia Huck if (linfo && desc && (linfo->align != 4096)) { 218a5cf2bb4SCornelia Huck return -EINVAL; 219a5cf2bb4SCornelia Huck } 220a5cf2bb4SCornelia Huck 221f24a6840SPaolo Bonzini if (!vdev) { 222a5cf2bb4SCornelia Huck return -EINVAL; 223a5cf2bb4SCornelia Huck } 224a5cf2bb4SCornelia Huck 2250db87e0dSCornelia Huck if (info) { 2260db87e0dSCornelia Huck virtio_queue_set_rings(vdev, index, desc, info->avail, info->used); 2270db87e0dSCornelia Huck } else { 2280db87e0dSCornelia Huck virtio_queue_set_addr(vdev, index, desc); 2290db87e0dSCornelia Huck } 2300db87e0dSCornelia Huck if (!desc) { 231955cc8c9SJason Wang virtio_queue_set_vector(vdev, index, VIRTIO_NO_VECTOR); 232a5cf2bb4SCornelia Huck } else { 23379cd0c80SCornelia Huck if (info) { 23479cd0c80SCornelia Huck /* virtio-1 allows changing the ring size. */ 2358c797e75SMichael S. Tsirkin if (virtio_queue_get_max_num(vdev, index) < num) { 23679cd0c80SCornelia Huck /* Fail if we exceed the maximum number. */ 237a5cf2bb4SCornelia Huck return -EINVAL; 238a5cf2bb4SCornelia Huck } 23979cd0c80SCornelia Huck virtio_queue_set_num(vdev, index, num); 24079cd0c80SCornelia Huck } else if (virtio_queue_get_num(vdev, index) > num) { 24179cd0c80SCornelia Huck /* Fail if we don't have a big enough queue. */ 24279cd0c80SCornelia Huck return -EINVAL; 24379cd0c80SCornelia Huck } 24479cd0c80SCornelia Huck /* We ignore possible increased num for legacy for compatibility. */ 245f24a6840SPaolo Bonzini virtio_queue_set_vector(vdev, index, index); 246a5cf2bb4SCornelia Huck } 247a5cf2bb4SCornelia Huck /* tell notify handler in case of config change */ 248b1914b82SHalil Pasic vdev->config_vector = VIRTIO_QUEUE_MAX; 249a5cf2bb4SCornelia Huck return 0; 250a5cf2bb4SCornelia Huck } 251a5cf2bb4SCornelia Huck 252997340f3SPaolo Bonzini static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev) 253fa8b0ca5SCornelia Huck { 254b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev); 255b804e8a6SJing Liu 256fa8b0ca5SCornelia Huck virtio_ccw_stop_ioeventfd(dev); 257997340f3SPaolo Bonzini virtio_bus_reset(&dev->bus); 258fa8b0ca5SCornelia Huck if (dev->indicators) { 259fa8b0ca5SCornelia Huck release_indicator(&dev->routes.adapter, dev->indicators); 260fa8b0ca5SCornelia Huck dev->indicators = NULL; 261fa8b0ca5SCornelia Huck } 262fa8b0ca5SCornelia Huck if (dev->indicators2) { 263fa8b0ca5SCornelia Huck release_indicator(&dev->routes.adapter, dev->indicators2); 264fa8b0ca5SCornelia Huck dev->indicators2 = NULL; 265fa8b0ca5SCornelia Huck } 266fa8b0ca5SCornelia Huck if (dev->summary_indicator) { 267fa8b0ca5SCornelia Huck release_indicator(&dev->routes.adapter, dev->summary_indicator); 268fa8b0ca5SCornelia Huck dev->summary_indicator = NULL; 269fa8b0ca5SCornelia Huck } 270b804e8a6SJing Liu ccw_dev->sch->thinint_active = false; 271fa8b0ca5SCornelia Huck } 272fa8b0ca5SCornelia Huck 2730db87e0dSCornelia Huck static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len, 2740db87e0dSCornelia Huck bool is_legacy) 275a5cf2bb4SCornelia Huck { 276a5cf2bb4SCornelia Huck int ret; 277a5cf2bb4SCornelia Huck VqInfoBlock info; 2780db87e0dSCornelia Huck VqInfoBlockLegacy linfo; 2790db87e0dSCornelia Huck size_t info_len = is_legacy ? sizeof(linfo) : sizeof(info); 2800db87e0dSCornelia Huck 2810db87e0dSCornelia Huck if (check_len) { 2820db87e0dSCornelia Huck if (ccw.count != info_len) { 2830db87e0dSCornelia Huck return -EINVAL; 2840db87e0dSCornelia Huck } 2850db87e0dSCornelia Huck } else if (ccw.count < info_len) { 2860db87e0dSCornelia Huck /* Can't execute command. */ 2870db87e0dSCornelia Huck return -EINVAL; 2880db87e0dSCornelia Huck } 2890db87e0dSCornelia Huck if (!ccw.cda) { 2900db87e0dSCornelia Huck return -EFAULT; 2910db87e0dSCornelia Huck } 2920db87e0dSCornelia Huck if (is_legacy) { 293d895d25aSPierre Morel ret = ccw_dstream_read(&sch->cds, linfo); 294d895d25aSPierre Morel if (ret) { 295d895d25aSPierre Morel return ret; 296d895d25aSPierre Morel } 297c9aacaadSPeter Maydell linfo.queue = be64_to_cpu(linfo.queue); 298c9aacaadSPeter Maydell linfo.align = be32_to_cpu(linfo.align); 299c9aacaadSPeter Maydell linfo.index = be16_to_cpu(linfo.index); 300c9aacaadSPeter Maydell linfo.num = be16_to_cpu(linfo.num); 3010db87e0dSCornelia Huck ret = virtio_ccw_set_vqs(sch, NULL, &linfo); 3020db87e0dSCornelia Huck } else { 303d895d25aSPierre Morel ret = ccw_dstream_read(&sch->cds, info); 304d895d25aSPierre Morel if (ret) { 305d895d25aSPierre Morel return ret; 306d895d25aSPierre Morel } 307c9aacaadSPeter Maydell info.desc = be64_to_cpu(info.desc); 308c9aacaadSPeter Maydell info.index = be16_to_cpu(info.index); 309c9aacaadSPeter Maydell info.num = be16_to_cpu(info.num); 310c9aacaadSPeter Maydell info.avail = be64_to_cpu(info.avail); 311c9aacaadSPeter Maydell info.used = be64_to_cpu(info.used); 3120db87e0dSCornelia Huck ret = virtio_ccw_set_vqs(sch, &info, NULL); 3130db87e0dSCornelia Huck } 3140db87e0dSCornelia Huck sch->curr_status.scsw.count = 0; 3150db87e0dSCornelia Huck return ret; 3160db87e0dSCornelia Huck } 3170db87e0dSCornelia Huck 3180db87e0dSCornelia Huck static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) 3190db87e0dSCornelia Huck { 3200db87e0dSCornelia Huck int ret; 321c42767f2SThomas Huth VirtioRevInfo revinfo; 322a5cf2bb4SCornelia Huck uint8_t status; 323a5cf2bb4SCornelia Huck VirtioFeatDesc features; 324a5cf2bb4SCornelia Huck hwaddr indicators; 325a5cf2bb4SCornelia Huck VqConfigBlock vq_config; 326a5cf2bb4SCornelia Huck VirtioCcwDevice *dev = sch->driver_data; 327f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_ccw_get_vdev(sch); 328a5cf2bb4SCornelia Huck bool check_len; 329a5cf2bb4SCornelia Huck int len; 330f57ba058SHalil Pasic VirtioThinintInfo thinint; 331a5cf2bb4SCornelia Huck 332a5cf2bb4SCornelia Huck if (!dev) { 333a5cf2bb4SCornelia Huck return -EINVAL; 334a5cf2bb4SCornelia Huck } 335a5cf2bb4SCornelia Huck 336a5cf2bb4SCornelia Huck trace_virtio_ccw_interpret_ccw(sch->cssid, sch->ssid, sch->schid, 337a5cf2bb4SCornelia Huck ccw.cmd_code); 338a5cf2bb4SCornelia Huck check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); 339a5cf2bb4SCornelia Huck 340151fcdfdSCornelia Huck if (dev->revision < 0 && ccw.cmd_code != CCW_CMD_SET_VIRTIO_REV) { 341151fcdfdSCornelia Huck if (dev->force_revision_1) { 34247e13dfdSHalil Pasic /* 34347e13dfdSHalil Pasic * virtio-1 drivers must start with negotiating to a revision >= 1, 34447e13dfdSHalil Pasic * so post a command reject for all other commands 34547e13dfdSHalil Pasic */ 34647e13dfdSHalil Pasic return -ENOSYS; 347151fcdfdSCornelia Huck } else { 348151fcdfdSCornelia Huck /* 349151fcdfdSCornelia Huck * If the driver issues any command that is not SET_VIRTIO_REV, 350151fcdfdSCornelia Huck * we'll have to operate the device in legacy mode. 351151fcdfdSCornelia Huck */ 352151fcdfdSCornelia Huck dev->revision = 0; 353151fcdfdSCornelia Huck } 35447e13dfdSHalil Pasic } 35547e13dfdSHalil Pasic 356a5cf2bb4SCornelia Huck /* Look at the command. */ 357a5cf2bb4SCornelia Huck switch (ccw.cmd_code) { 358a5cf2bb4SCornelia Huck case CCW_CMD_SET_VQ: 3590db87e0dSCornelia Huck ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1); 360a5cf2bb4SCornelia Huck break; 361a5cf2bb4SCornelia Huck case CCW_CMD_VDEV_RESET: 362997340f3SPaolo Bonzini virtio_ccw_reset_virtio(dev); 363a5cf2bb4SCornelia Huck ret = 0; 364a5cf2bb4SCornelia Huck break; 365a5cf2bb4SCornelia Huck case CCW_CMD_READ_FEAT: 366a5cf2bb4SCornelia Huck if (check_len) { 367a5cf2bb4SCornelia Huck if (ccw.count != sizeof(features)) { 368a5cf2bb4SCornelia Huck ret = -EINVAL; 369a5cf2bb4SCornelia Huck break; 370a5cf2bb4SCornelia Huck } 371a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(features)) { 372a5cf2bb4SCornelia Huck /* Can't execute command. */ 373a5cf2bb4SCornelia Huck ret = -EINVAL; 374a5cf2bb4SCornelia Huck break; 375a5cf2bb4SCornelia Huck } 376a5cf2bb4SCornelia Huck if (!ccw.cda) { 377a5cf2bb4SCornelia Huck ret = -EFAULT; 378a5cf2bb4SCornelia Huck } else { 3799b706dbbSMichael S. Tsirkin VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); 3809b706dbbSMichael S. Tsirkin 381f57ba058SHalil Pasic ccw_dstream_advance(&sch->cds, sizeof(features.features)); 382d895d25aSPierre Morel ret = ccw_dstream_read(&sch->cds, features.index); 383d895d25aSPierre Morel if (ret) { 384d895d25aSPierre Morel break; 385d895d25aSPierre Morel } 3866b8f1020SCornelia Huck if (features.index == 0) { 387542571d5SCornelia Huck if (dev->revision >= 1) { 388542571d5SCornelia Huck /* Don't offer legacy features for modern devices. */ 389542571d5SCornelia Huck features.features = (uint32_t) 3909b706dbbSMichael S. Tsirkin (vdev->host_features & ~vdc->legacy_features); 391542571d5SCornelia Huck } else { 392c42767f2SThomas Huth features.features = (uint32_t)vdev->host_features; 393542571d5SCornelia Huck } 394b4f8f9dfSCornelia Huck } else if ((features.index == 1) && (dev->revision >= 1)) { 395c42767f2SThomas Huth /* 396b4f8f9dfSCornelia Huck * Only offer feature bits beyond 31 if the guest has 397b4f8f9dfSCornelia Huck * negotiated at least revision 1. 398c42767f2SThomas Huth */ 399b4f8f9dfSCornelia Huck features.features = (uint32_t)(vdev->host_features >> 32); 400a5cf2bb4SCornelia Huck } else { 401a5cf2bb4SCornelia Huck /* Return zeroes if the guest supports more feature bits. */ 402a5cf2bb4SCornelia Huck features.features = 0; 403a5cf2bb4SCornelia Huck } 404f57ba058SHalil Pasic ccw_dstream_rewind(&sch->cds); 405c9aacaadSPeter Maydell features.features = cpu_to_le32(features.features); 406d895d25aSPierre Morel ret = ccw_dstream_write(&sch->cds, features.features); 407d895d25aSPierre Morel if (!ret) { 408a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(features); 409d895d25aSPierre Morel } 410a5cf2bb4SCornelia Huck } 411a5cf2bb4SCornelia Huck break; 412a5cf2bb4SCornelia Huck case CCW_CMD_WRITE_FEAT: 413a5cf2bb4SCornelia Huck if (check_len) { 414a5cf2bb4SCornelia Huck if (ccw.count != sizeof(features)) { 415a5cf2bb4SCornelia Huck ret = -EINVAL; 416a5cf2bb4SCornelia Huck break; 417a5cf2bb4SCornelia Huck } 418a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(features)) { 419a5cf2bb4SCornelia Huck /* Can't execute command. */ 420a5cf2bb4SCornelia Huck ret = -EINVAL; 421a5cf2bb4SCornelia Huck break; 422a5cf2bb4SCornelia Huck } 423a5cf2bb4SCornelia Huck if (!ccw.cda) { 424a5cf2bb4SCornelia Huck ret = -EFAULT; 425a5cf2bb4SCornelia Huck } else { 426d895d25aSPierre Morel ret = ccw_dstream_read(&sch->cds, features); 427d895d25aSPierre Morel if (ret) { 428d895d25aSPierre Morel break; 429d895d25aSPierre Morel } 430c9aacaadSPeter Maydell features.features = le32_to_cpu(features.features); 4316b8f1020SCornelia Huck if (features.index == 0) { 432c42767f2SThomas Huth virtio_set_features(vdev, 433c42767f2SThomas Huth (vdev->guest_features & 0xffffffff00000000ULL) | 434c42767f2SThomas Huth features.features); 435b4f8f9dfSCornelia Huck } else if ((features.index == 1) && (dev->revision >= 1)) { 436c42767f2SThomas Huth /* 437b4f8f9dfSCornelia Huck * If the guest did not negotiate at least revision 1, 438b4f8f9dfSCornelia Huck * we did not offer it any feature bits beyond 31. Such a 439b4f8f9dfSCornelia Huck * guest passing us any bit here is therefore buggy. 440c42767f2SThomas Huth */ 441c42767f2SThomas Huth virtio_set_features(vdev, 442c42767f2SThomas Huth (vdev->guest_features & 0x00000000ffffffffULL) | 443c42767f2SThomas Huth ((uint64_t)features.features << 32)); 444a5cf2bb4SCornelia Huck } else { 445a5cf2bb4SCornelia Huck /* 446a5cf2bb4SCornelia Huck * If the guest supports more feature bits, assert that it 447a5cf2bb4SCornelia Huck * passes us zeroes for those we don't support. 448a5cf2bb4SCornelia Huck */ 449a5cf2bb4SCornelia Huck if (features.features) { 45074a69e03SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 45174a69e03SAlistair Francis "Guest bug: features[%i]=%x (expected 0)", 452a5cf2bb4SCornelia Huck features.index, features.features); 453a5cf2bb4SCornelia Huck /* XXX: do a unit check here? */ 454a5cf2bb4SCornelia Huck } 455a5cf2bb4SCornelia Huck } 456a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(features); 457a5cf2bb4SCornelia Huck ret = 0; 458a5cf2bb4SCornelia Huck } 459a5cf2bb4SCornelia Huck break; 460a5cf2bb4SCornelia Huck case CCW_CMD_READ_CONF: 461a5cf2bb4SCornelia Huck if (check_len) { 462f24a6840SPaolo Bonzini if (ccw.count > vdev->config_len) { 463a5cf2bb4SCornelia Huck ret = -EINVAL; 464a5cf2bb4SCornelia Huck break; 465a5cf2bb4SCornelia Huck } 466a5cf2bb4SCornelia Huck } 467f24a6840SPaolo Bonzini len = MIN(ccw.count, vdev->config_len); 468a5cf2bb4SCornelia Huck if (!ccw.cda) { 469a5cf2bb4SCornelia Huck ret = -EFAULT; 470a5cf2bb4SCornelia Huck } else { 471f24a6840SPaolo Bonzini virtio_bus_get_vdev_config(&dev->bus, vdev->config); 472d895d25aSPierre Morel ret = ccw_dstream_write_buf(&sch->cds, vdev->config, len); 473d895d25aSPierre Morel if (ret) { 474a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - len; 475d895d25aSPierre Morel } 476a5cf2bb4SCornelia Huck } 477a5cf2bb4SCornelia Huck break; 478a5cf2bb4SCornelia Huck case CCW_CMD_WRITE_CONF: 479a5cf2bb4SCornelia Huck if (check_len) { 480f24a6840SPaolo Bonzini if (ccw.count > vdev->config_len) { 481a5cf2bb4SCornelia Huck ret = -EINVAL; 482a5cf2bb4SCornelia Huck break; 483a5cf2bb4SCornelia Huck } 484a5cf2bb4SCornelia Huck } 485f24a6840SPaolo Bonzini len = MIN(ccw.count, vdev->config_len); 486a5cf2bb4SCornelia Huck if (!ccw.cda) { 487a5cf2bb4SCornelia Huck ret = -EFAULT; 488a5cf2bb4SCornelia Huck } else { 489f57ba058SHalil Pasic ret = ccw_dstream_read_buf(&sch->cds, vdev->config, len); 490f57ba058SHalil Pasic if (!ret) { 491f24a6840SPaolo Bonzini virtio_bus_set_vdev_config(&dev->bus, vdev->config); 492a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - len; 493a5cf2bb4SCornelia Huck } 494a5cf2bb4SCornelia Huck } 495a5cf2bb4SCornelia Huck break; 496e32652f7SPierre Morel case CCW_CMD_READ_STATUS: 497e32652f7SPierre Morel if (check_len) { 498e32652f7SPierre Morel if (ccw.count != sizeof(status)) { 499e32652f7SPierre Morel ret = -EINVAL; 500e32652f7SPierre Morel break; 501e32652f7SPierre Morel } 502e32652f7SPierre Morel } else if (ccw.count < sizeof(status)) { 503e32652f7SPierre Morel /* Can't execute command. */ 504e32652f7SPierre Morel ret = -EINVAL; 505e32652f7SPierre Morel break; 506e32652f7SPierre Morel } 507e32652f7SPierre Morel if (!ccw.cda) { 508e32652f7SPierre Morel ret = -EFAULT; 509e32652f7SPierre Morel } else { 510e32652f7SPierre Morel address_space_stb(&address_space_memory, ccw.cda, vdev->status, 511e32652f7SPierre Morel MEMTXATTRS_UNSPECIFIED, NULL); 5123c254ab8SLadi Prosek sch->curr_status.scsw.count = ccw.count - sizeof(vdev->status); 513e32652f7SPierre Morel ret = 0; 514e32652f7SPierre Morel } 515e32652f7SPierre Morel break; 516a5cf2bb4SCornelia Huck case CCW_CMD_WRITE_STATUS: 517a5cf2bb4SCornelia Huck if (check_len) { 518a5cf2bb4SCornelia Huck if (ccw.count != sizeof(status)) { 519a5cf2bb4SCornelia Huck ret = -EINVAL; 520a5cf2bb4SCornelia Huck break; 521a5cf2bb4SCornelia Huck } 522a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(status)) { 523a5cf2bb4SCornelia Huck /* Can't execute command. */ 524a5cf2bb4SCornelia Huck ret = -EINVAL; 525a5cf2bb4SCornelia Huck break; 526a5cf2bb4SCornelia Huck } 527a5cf2bb4SCornelia Huck if (!ccw.cda) { 528a5cf2bb4SCornelia Huck ret = -EFAULT; 529a5cf2bb4SCornelia Huck } else { 530d895d25aSPierre Morel ret = ccw_dstream_read(&sch->cds, status); 531d895d25aSPierre Morel if (ret) { 532d895d25aSPierre Morel break; 533d895d25aSPierre Morel } 534b4436a0bSCornelia Huck if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { 535b4436a0bSCornelia Huck virtio_ccw_stop_ioeventfd(dev); 536b4436a0bSCornelia Huck } 5370b352fd6SCornelia Huck if (virtio_set_status(vdev, status) == 0) { 538f24a6840SPaolo Bonzini if (vdev->status == 0) { 539997340f3SPaolo Bonzini virtio_ccw_reset_virtio(dev); 540a5cf2bb4SCornelia Huck } 541b4436a0bSCornelia Huck if (status & VIRTIO_CONFIG_S_DRIVER_OK) { 542b4436a0bSCornelia Huck virtio_ccw_start_ioeventfd(dev); 543b4436a0bSCornelia Huck } 544a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(status); 545a5cf2bb4SCornelia Huck ret = 0; 5460b352fd6SCornelia Huck } else { 5470b352fd6SCornelia Huck /* Trigger a command reject. */ 5480b352fd6SCornelia Huck ret = -ENOSYS; 5490b352fd6SCornelia Huck } 550a5cf2bb4SCornelia Huck } 551a5cf2bb4SCornelia Huck break; 552a5cf2bb4SCornelia Huck case CCW_CMD_SET_IND: 553a5cf2bb4SCornelia Huck if (check_len) { 554a5cf2bb4SCornelia Huck if (ccw.count != sizeof(indicators)) { 555a5cf2bb4SCornelia Huck ret = -EINVAL; 556a5cf2bb4SCornelia Huck break; 557a5cf2bb4SCornelia Huck } 558a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(indicators)) { 559a5cf2bb4SCornelia Huck /* Can't execute command. */ 560a5cf2bb4SCornelia Huck ret = -EINVAL; 561a5cf2bb4SCornelia Huck break; 562a5cf2bb4SCornelia Huck } 5637e749462SCornelia Huck if (sch->thinint_active) { 5647e749462SCornelia Huck /* Trigger a command reject. */ 5657e749462SCornelia Huck ret = -ENOSYS; 5667e749462SCornelia Huck break; 5677e749462SCornelia Huck } 568797b6086SHalil Pasic if (virtio_get_num_queues(vdev) > NR_CLASSIC_INDICATOR_BITS) { 569797b6086SHalil Pasic /* More queues than indicator bits --> trigger a reject */ 570797b6086SHalil Pasic ret = -ENOSYS; 571797b6086SHalil Pasic break; 572797b6086SHalil Pasic } 573d1db1fa8SCornelia Huck if (!ccw.cda) { 574a5cf2bb4SCornelia Huck ret = -EFAULT; 575a5cf2bb4SCornelia Huck } else { 576d895d25aSPierre Morel ret = ccw_dstream_read(&sch->cds, indicators); 577d895d25aSPierre Morel if (ret) { 578d895d25aSPierre Morel break; 579d895d25aSPierre Morel } 580c9aacaadSPeter Maydell indicators = be64_to_cpu(indicators); 5817bca3892SCornelia Huck dev->indicators = get_indicator(indicators, sizeof(uint64_t)); 582a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(indicators); 583a5cf2bb4SCornelia Huck ret = 0; 584a5cf2bb4SCornelia Huck } 585a5cf2bb4SCornelia Huck break; 586a5cf2bb4SCornelia Huck case CCW_CMD_SET_CONF_IND: 587a5cf2bb4SCornelia Huck if (check_len) { 588a5cf2bb4SCornelia Huck if (ccw.count != sizeof(indicators)) { 589a5cf2bb4SCornelia Huck ret = -EINVAL; 590a5cf2bb4SCornelia Huck break; 591a5cf2bb4SCornelia Huck } 592a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(indicators)) { 593a5cf2bb4SCornelia Huck /* Can't execute command. */ 594a5cf2bb4SCornelia Huck ret = -EINVAL; 595a5cf2bb4SCornelia Huck break; 596a5cf2bb4SCornelia Huck } 597d1db1fa8SCornelia Huck if (!ccw.cda) { 598a5cf2bb4SCornelia Huck ret = -EFAULT; 599a5cf2bb4SCornelia Huck } else { 600d895d25aSPierre Morel ret = ccw_dstream_read(&sch->cds, indicators); 601d895d25aSPierre Morel if (ret) { 602d895d25aSPierre Morel break; 603d895d25aSPierre Morel } 604c9aacaadSPeter Maydell indicators = be64_to_cpu(indicators); 6057bca3892SCornelia Huck dev->indicators2 = get_indicator(indicators, sizeof(uint64_t)); 606a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(indicators); 607a5cf2bb4SCornelia Huck ret = 0; 608a5cf2bb4SCornelia Huck } 609a5cf2bb4SCornelia Huck break; 610a5cf2bb4SCornelia Huck case CCW_CMD_READ_VQ_CONF: 611a5cf2bb4SCornelia Huck if (check_len) { 612a5cf2bb4SCornelia Huck if (ccw.count != sizeof(vq_config)) { 613a5cf2bb4SCornelia Huck ret = -EINVAL; 614a5cf2bb4SCornelia Huck break; 615a5cf2bb4SCornelia Huck } 616a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(vq_config)) { 617a5cf2bb4SCornelia Huck /* Can't execute command. */ 618a5cf2bb4SCornelia Huck ret = -EINVAL; 619a5cf2bb4SCornelia Huck break; 620a5cf2bb4SCornelia Huck } 621a5cf2bb4SCornelia Huck if (!ccw.cda) { 622a5cf2bb4SCornelia Huck ret = -EFAULT; 623a5cf2bb4SCornelia Huck } else { 624d895d25aSPierre Morel ret = ccw_dstream_read(&sch->cds, vq_config.index); 625d895d25aSPierre Morel if (ret) { 626d895d25aSPierre Morel break; 627d895d25aSPierre Morel } 628c9aacaadSPeter Maydell vq_config.index = be16_to_cpu(vq_config.index); 629b1914b82SHalil Pasic if (vq_config.index >= VIRTIO_QUEUE_MAX) { 630d03a3630SCornelia Huck ret = -EINVAL; 631d03a3630SCornelia Huck break; 632d03a3630SCornelia Huck } 633f24a6840SPaolo Bonzini vq_config.num_max = virtio_queue_get_num(vdev, 634a5cf2bb4SCornelia Huck vq_config.index); 635c9aacaadSPeter Maydell vq_config.num_max = cpu_to_be16(vq_config.num_max); 636d895d25aSPierre Morel ret = ccw_dstream_write(&sch->cds, vq_config.num_max); 637d895d25aSPierre Morel if (!ret) { 638a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(vq_config); 639d895d25aSPierre Morel } 640a5cf2bb4SCornelia Huck } 641a5cf2bb4SCornelia Huck break; 6427e749462SCornelia Huck case CCW_CMD_SET_IND_ADAPTER: 6437e749462SCornelia Huck if (check_len) { 644f57ba058SHalil Pasic if (ccw.count != sizeof(thinint)) { 6457e749462SCornelia Huck ret = -EINVAL; 6467e749462SCornelia Huck break; 6477e749462SCornelia Huck } 648f57ba058SHalil Pasic } else if (ccw.count < sizeof(thinint)) { 6497e749462SCornelia Huck /* Can't execute command. */ 6507e749462SCornelia Huck ret = -EINVAL; 6517e749462SCornelia Huck break; 6527e749462SCornelia Huck } 6537e749462SCornelia Huck if (!ccw.cda) { 6547e749462SCornelia Huck ret = -EFAULT; 6557e749462SCornelia Huck } else if (dev->indicators && !sch->thinint_active) { 6567e749462SCornelia Huck /* Trigger a command reject. */ 6577e749462SCornelia Huck ret = -ENOSYS; 6587e749462SCornelia Huck } else { 659f57ba058SHalil Pasic if (ccw_dstream_read(&sch->cds, thinint)) { 6607e749462SCornelia Huck ret = -EFAULT; 6617e749462SCornelia Huck } else { 662c9aacaadSPeter Maydell thinint.ind_bit = be64_to_cpu(thinint.ind_bit); 663c9aacaadSPeter Maydell thinint.summary_indicator = 664c9aacaadSPeter Maydell be64_to_cpu(thinint.summary_indicator); 665c9aacaadSPeter Maydell thinint.device_indicator = 666c9aacaadSPeter Maydell be64_to_cpu(thinint.device_indicator); 6677d45285fSCornelia Huck 6687bca3892SCornelia Huck dev->summary_indicator = 669f57ba058SHalil Pasic get_indicator(thinint.summary_indicator, sizeof(uint8_t)); 6707d45285fSCornelia Huck dev->indicators = 671f57ba058SHalil Pasic get_indicator(thinint.device_indicator, 672f57ba058SHalil Pasic thinint.ind_bit / 8 + 1); 673f57ba058SHalil Pasic dev->thinint_isc = thinint.isc; 674f57ba058SHalil Pasic dev->routes.adapter.ind_offset = thinint.ind_bit; 675d426d9fbSCornelia Huck dev->routes.adapter.summary_offset = 7; 676dde522bbSFei Li dev->routes.adapter.adapter_id = css_get_adapter_id( 677dde522bbSFei Li CSS_IO_ADAPTER_VIRTIO, 678dde522bbSFei Li dev->thinint_isc); 6797bca3892SCornelia Huck sch->thinint_active = ((dev->indicators != NULL) && 6807bca3892SCornelia Huck (dev->summary_indicator != NULL)); 681f57ba058SHalil Pasic sch->curr_status.scsw.count = ccw.count - sizeof(thinint); 6827e749462SCornelia Huck ret = 0; 6837e749462SCornelia Huck } 6847e749462SCornelia Huck } 6857e749462SCornelia Huck break; 686c42767f2SThomas Huth case CCW_CMD_SET_VIRTIO_REV: 687c42767f2SThomas Huth len = sizeof(revinfo); 688c42767f2SThomas Huth if (ccw.count < len) { 689c42767f2SThomas Huth ret = -EINVAL; 690c42767f2SThomas Huth break; 691c42767f2SThomas Huth } 692c42767f2SThomas Huth if (!ccw.cda) { 693c42767f2SThomas Huth ret = -EFAULT; 694c42767f2SThomas Huth break; 695c42767f2SThomas Huth } 696d895d25aSPierre Morel ret = ccw_dstream_read_buf(&sch->cds, &revinfo, 4); 697d895d25aSPierre Morel if (ret < 0) { 698d895d25aSPierre Morel break; 699d895d25aSPierre Morel } 700c9aacaadSPeter Maydell revinfo.revision = be16_to_cpu(revinfo.revision); 701c9aacaadSPeter Maydell revinfo.length = be16_to_cpu(revinfo.length); 702c42767f2SThomas Huth if (ccw.count < len + revinfo.length || 703c42767f2SThomas Huth (check_len && ccw.count > len + revinfo.length)) { 704c42767f2SThomas Huth ret = -EINVAL; 705c42767f2SThomas Huth break; 706c42767f2SThomas Huth } 707c42767f2SThomas Huth /* 708c42767f2SThomas Huth * Once we start to support revisions with additional data, we'll 709c42767f2SThomas Huth * need to fetch it here. Nothing to do for now, though. 710c42767f2SThomas Huth */ 711c42767f2SThomas Huth if (dev->revision >= 0 || 71247e13dfdSHalil Pasic revinfo.revision > virtio_ccw_rev_max(dev) || 71347e13dfdSHalil Pasic (dev->force_revision_1 && !revinfo.revision)) { 714c42767f2SThomas Huth ret = -ENOSYS; 715c42767f2SThomas Huth break; 716c42767f2SThomas Huth } 717c42767f2SThomas Huth ret = 0; 718c42767f2SThomas Huth dev->revision = revinfo.revision; 719c42767f2SThomas Huth break; 720a5cf2bb4SCornelia Huck default: 7218d034a6fSCornelia Huck ret = -ENOSYS; 722a5cf2bb4SCornelia Huck break; 723a5cf2bb4SCornelia Huck } 724a5cf2bb4SCornelia Huck return ret; 725a5cf2bb4SCornelia Huck } 726a5cf2bb4SCornelia Huck 727c42767f2SThomas Huth static void virtio_sch_disable_cb(SubchDev *sch) 728c42767f2SThomas Huth { 729c42767f2SThomas Huth VirtioCcwDevice *dev = sch->driver_data; 730c42767f2SThomas Huth 731c42767f2SThomas Huth dev->revision = -1; 732c42767f2SThomas Huth } 733c42767f2SThomas Huth 7341fa75523SCornelia Huck static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) 735a5cf2bb4SCornelia Huck { 7361fa75523SCornelia Huck VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev); 737b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev); 738d8d98db5SDong Jia Shi CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); 739817d4a6bSDong Jia Shi SubchDev *sch; 740cf249935SSascha Silbe Error *err = NULL; 7413c5fd807SCornelia Huck int i; 742a5cf2bb4SCornelia Huck 74336699ab4SCornelia Huck sch = css_create_sch(ccw_dev->devno, errp); 744cf249935SSascha Silbe if (!sch) { 745cf249935SSascha Silbe return; 746cf249935SSascha Silbe } 74747e13dfdSHalil Pasic if (!virtio_ccw_rev_max(dev) && dev->force_revision_1) { 74847e13dfdSHalil Pasic error_setg(&err, "Invalid value of property max_rev " 74947e13dfdSHalil Pasic "(is %d expected >= 1)", virtio_ccw_rev_max(dev)); 750d8d98db5SDong Jia Shi goto out_err; 75147e13dfdSHalil Pasic } 752a5cf2bb4SCornelia Huck 753a5cf2bb4SCornelia Huck sch->driver_data = dev; 754a5cf2bb4SCornelia Huck sch->ccw_cb = virtio_ccw_cb; 755c42767f2SThomas Huth sch->disable_cb = virtio_sch_disable_cb; 756a5cf2bb4SCornelia Huck sch->id.reserved = 0xff; 757a5cf2bb4SCornelia Huck sch->id.cu_type = VIRTIO_CCW_CU_TYPE; 758bab482d7SXiao Feng Ren sch->do_subchannel_work = do_subchannel_work_virtual; 7590599a046SEric Farman sch->irb_cb = build_irb_virtual; 760b804e8a6SJing Liu ccw_dev->sch = sch; 761cf249935SSascha Silbe dev->indicators = NULL; 762c42767f2SThomas Huth dev->revision = -1; 7633c5fd807SCornelia Huck for (i = 0; i < ADAPTER_ROUTES_MAX_GSI; i++) { 7643c5fd807SCornelia Huck dev->routes.gsi[i] = -1; 7653c5fd807SCornelia Huck } 766cf249935SSascha Silbe css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); 767cf249935SSascha Silbe 768cf249935SSascha Silbe trace_virtio_ccw_new_device( 769cf249935SSascha Silbe sch->cssid, sch->ssid, sch->schid, sch->devno, 7702a78ac66SDong Jia Shi ccw_dev->devno.valid ? "user-configured" : "auto-configured"); 771c42767f2SThomas Huth 772cda3c19fSQingFeng Hao if (kvm_enabled() && !kvm_eventfds_enabled()) { 773ca2b413cSPaolo Bonzini dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; 774ca2b413cSPaolo Bonzini } 775ca2b413cSPaolo Bonzini 7763909c079SPavel Dovgalyuk /* fd-based ioevents can't be synchronized in record/replay */ 7773909c079SPavel Dovgalyuk if (replay_mode != REPLAY_MODE_NONE) { 7783909c079SPavel Dovgalyuk dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; 7793909c079SPavel Dovgalyuk } 7803909c079SPavel Dovgalyuk 7811fa75523SCornelia Huck if (k->realize) { 7821fa75523SCornelia Huck k->realize(dev, &err); 7831fa75523SCornelia Huck if (err) { 784d8d98db5SDong Jia Shi goto out_err; 785d8d98db5SDong Jia Shi } 786d8d98db5SDong Jia Shi } 787d8d98db5SDong Jia Shi 788d8d98db5SDong Jia Shi ck->realize(ccw_dev, &err); 789d8d98db5SDong Jia Shi if (err) { 790d8d98db5SDong Jia Shi goto out_err; 791d8d98db5SDong Jia Shi } 792d8d98db5SDong Jia Shi 793d8d98db5SDong Jia Shi return; 794d8d98db5SDong Jia Shi 795d8d98db5SDong Jia Shi out_err: 7961fa75523SCornelia Huck error_propagate(errp, err); 79706e686eaSCornelia Huck css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); 798b804e8a6SJing Liu ccw_dev->sch = NULL; 799a5cf2bb4SCornelia Huck g_free(sch); 800a5cf2bb4SCornelia Huck } 801a5cf2bb4SCornelia Huck 802b69c3c21SMarkus Armbruster static void virtio_ccw_device_unrealize(VirtioCcwDevice *dev) 803a5cf2bb4SCornelia Huck { 8042d6ff33aSThomas Huth VirtIOCCWDeviceClass *dc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); 805b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev); 806b804e8a6SJing Liu SubchDev *sch = ccw_dev->sch; 807a5cf2bb4SCornelia Huck 8082d6ff33aSThomas Huth if (dc->unrealize) { 809b69c3c21SMarkus Armbruster dc->unrealize(dev); 8102d6ff33aSThomas Huth } 8112d6ff33aSThomas Huth 812a5cf2bb4SCornelia Huck if (sch) { 813a5cf2bb4SCornelia Huck css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); 814a5cf2bb4SCornelia Huck g_free(sch); 81524118af8SNia Alarie ccw_dev->sch = NULL; 816a5cf2bb4SCornelia Huck } 8177bca3892SCornelia Huck if (dev->indicators) { 818d426d9fbSCornelia Huck release_indicator(&dev->routes.adapter, dev->indicators); 8197bca3892SCornelia Huck dev->indicators = NULL; 8207bca3892SCornelia Huck } 821a5cf2bb4SCornelia Huck } 822a5cf2bb4SCornelia Huck 823a5cf2bb4SCornelia Huck /* DeviceState to VirtioCcwDevice. Note: used on datapath, 824a5cf2bb4SCornelia Huck * be careful and test performance if you change this. 825a5cf2bb4SCornelia Huck */ 826a5cf2bb4SCornelia Huck static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) 827a5cf2bb4SCornelia Huck { 828b804e8a6SJing Liu CcwDevice *ccw_dev = to_ccw_dev_fast(d); 829b804e8a6SJing Liu 830b804e8a6SJing Liu return container_of(ccw_dev, VirtioCcwDevice, parent_obj); 831a5cf2bb4SCornelia Huck } 832a5cf2bb4SCornelia Huck 8337e749462SCornelia Huck static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, 8347e749462SCornelia Huck uint8_t to_be_set) 8357e749462SCornelia Huck { 8361a8242f7SHalil Pasic uint8_t expected, actual; 8377e749462SCornelia Huck hwaddr len = 1; 8381a8242f7SHalil Pasic /* avoid multiple fetches */ 8391a8242f7SHalil Pasic uint8_t volatile *ind_addr; 8407e749462SCornelia Huck 84185eb7c18SPhilippe Mathieu-Daudé ind_addr = cpu_physical_memory_map(ind_loc, &len, true); 8427e749462SCornelia Huck if (!ind_addr) { 8437e749462SCornelia Huck error_report("%s(%x.%x.%04x): unable to access indicator", 8447e749462SCornelia Huck __func__, sch->cssid, sch->ssid, sch->schid); 8457e749462SCornelia Huck return -1; 8467e749462SCornelia Huck } 8471a8242f7SHalil Pasic actual = *ind_addr; 8487e749462SCornelia Huck do { 8491a8242f7SHalil Pasic expected = actual; 850d73415a3SStefan Hajnoczi actual = qatomic_cmpxchg(ind_addr, expected, expected | to_be_set); 8511a8242f7SHalil Pasic } while (actual != expected); 8521a8242f7SHalil Pasic trace_virtio_ccw_set_ind(ind_loc, actual, actual | to_be_set); 8531a8242f7SHalil Pasic cpu_physical_memory_unmap((void *)ind_addr, len, 1, len); 8547e749462SCornelia Huck 8551a8242f7SHalil Pasic return actual; 8567e749462SCornelia Huck } 8577e749462SCornelia Huck 858a5cf2bb4SCornelia Huck static void virtio_ccw_notify(DeviceState *d, uint16_t vector) 859a5cf2bb4SCornelia Huck { 860a5cf2bb4SCornelia Huck VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); 861b804e8a6SJing Liu CcwDevice *ccw_dev = to_ccw_dev_fast(d); 862b804e8a6SJing Liu SubchDev *sch = ccw_dev->sch; 863a5cf2bb4SCornelia Huck uint64_t indicators; 864a5cf2bb4SCornelia Huck 8657a5342e7SHalil Pasic if (vector == VIRTIO_NO_VECTOR) { 866a5cf2bb4SCornelia Huck return; 867a5cf2bb4SCornelia Huck } 8687a5342e7SHalil Pasic /* 8697a5342e7SHalil Pasic * vector < VIRTIO_QUEUE_MAX: notification for a virtqueue 8707a5342e7SHalil Pasic * vector == VIRTIO_QUEUE_MAX: configuration change notification 8717a5342e7SHalil Pasic * bits beyond that are unused and should never be notified for 8727a5342e7SHalil Pasic */ 8737a5342e7SHalil Pasic assert(vector <= VIRTIO_QUEUE_MAX); 874a5cf2bb4SCornelia Huck 875b1914b82SHalil Pasic if (vector < VIRTIO_QUEUE_MAX) { 8767c486976SCornelia Huck if (!dev->indicators) { 8777c486976SCornelia Huck return; 8787c486976SCornelia Huck } 8797e749462SCornelia Huck if (sch->thinint_active) { 8807e749462SCornelia Huck /* 8817e749462SCornelia Huck * In the adapter interrupt case, indicators points to a 8827e749462SCornelia Huck * memory area that may be (way) larger than 64 bit and 8837e749462SCornelia Huck * ind_bit indicates the start of the indicators in a big 8847e749462SCornelia Huck * endian notation. 8857e749462SCornelia Huck */ 886d426d9fbSCornelia Huck uint64_t ind_bit = dev->routes.adapter.ind_offset; 887d426d9fbSCornelia Huck 8887bca3892SCornelia Huck virtio_set_ind_atomic(sch, dev->indicators->addr + 889d426d9fbSCornelia Huck (ind_bit + vector) / 8, 890d426d9fbSCornelia Huck 0x80 >> ((ind_bit + vector) % 8)); 8917bca3892SCornelia Huck if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr, 8927e749462SCornelia Huck 0x01)) { 89325a08b8dSYi Min Zhao css_adapter_interrupt(CSS_IO_ADAPTER_VIRTIO, dev->thinint_isc); 8947e749462SCornelia Huck } 8957e749462SCornelia Huck } else { 8967a5342e7SHalil Pasic assert(vector < NR_CLASSIC_INDICATOR_BITS); 89742874d3aSPeter Maydell indicators = address_space_ldq(&address_space_memory, 89842874d3aSPeter Maydell dev->indicators->addr, 89942874d3aSPeter Maydell MEMTXATTRS_UNSPECIFIED, 90042874d3aSPeter Maydell NULL); 90119380b1bSCornelia Huck indicators |= 1ULL << vector; 90242874d3aSPeter Maydell address_space_stq(&address_space_memory, dev->indicators->addr, 90342874d3aSPeter Maydell indicators, MEMTXATTRS_UNSPECIFIED, NULL); 9047e749462SCornelia Huck css_conditional_io_interrupt(sch); 9057e749462SCornelia Huck } 906a5cf2bb4SCornelia Huck } else { 9077c486976SCornelia Huck if (!dev->indicators2) { 9087c486976SCornelia Huck return; 9097c486976SCornelia Huck } 91042874d3aSPeter Maydell indicators = address_space_ldq(&address_space_memory, 91142874d3aSPeter Maydell dev->indicators2->addr, 91242874d3aSPeter Maydell MEMTXATTRS_UNSPECIFIED, 91342874d3aSPeter Maydell NULL); 9147a5342e7SHalil Pasic indicators |= 1ULL; 91542874d3aSPeter Maydell address_space_stq(&address_space_memory, dev->indicators2->addr, 91642874d3aSPeter Maydell indicators, MEMTXATTRS_UNSPECIFIED, NULL); 917a5cf2bb4SCornelia Huck css_conditional_io_interrupt(sch); 9187e749462SCornelia Huck } 919a5cf2bb4SCornelia Huck } 920a5cf2bb4SCornelia Huck 921a5cf2bb4SCornelia Huck static void virtio_ccw_reset(DeviceState *d) 922a5cf2bb4SCornelia Huck { 923a5cf2bb4SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 924838fb84fSCornelia Huck VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); 925a5cf2bb4SCornelia Huck 926997340f3SPaolo Bonzini virtio_ccw_reset_virtio(dev); 927838fb84fSCornelia Huck if (vdc->parent_reset) { 928838fb84fSCornelia Huck vdc->parent_reset(d); 929838fb84fSCornelia Huck } 930a5cf2bb4SCornelia Huck } 931a5cf2bb4SCornelia Huck 932b4436a0bSCornelia Huck static void virtio_ccw_vmstate_change(DeviceState *d, bool running) 933b4436a0bSCornelia Huck { 934b4436a0bSCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 935b4436a0bSCornelia Huck 936b4436a0bSCornelia Huck if (running) { 937b4436a0bSCornelia Huck virtio_ccw_start_ioeventfd(dev); 938b4436a0bSCornelia Huck } else { 939b4436a0bSCornelia Huck virtio_ccw_stop_ioeventfd(dev); 940b4436a0bSCornelia Huck } 941b4436a0bSCornelia Huck } 942b4436a0bSCornelia Huck 943320ce850SCornelia Huck static bool virtio_ccw_query_guest_notifiers(DeviceState *d) 944320ce850SCornelia Huck { 945b804e8a6SJing Liu CcwDevice *dev = CCW_DEVICE(d); 946320ce850SCornelia Huck 947320ce850SCornelia Huck return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA); 948320ce850SCornelia Huck } 949320ce850SCornelia Huck 950d426d9fbSCornelia Huck static int virtio_ccw_get_mappings(VirtioCcwDevice *dev) 951d426d9fbSCornelia Huck { 952d426d9fbSCornelia Huck int r; 953b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev); 954d426d9fbSCornelia Huck 955b804e8a6SJing Liu if (!ccw_dev->sch->thinint_active) { 956d426d9fbSCornelia Huck return -EINVAL; 957d426d9fbSCornelia Huck } 958d426d9fbSCornelia Huck 959d426d9fbSCornelia Huck r = map_indicator(&dev->routes.adapter, dev->summary_indicator); 960d426d9fbSCornelia Huck if (r) { 961d426d9fbSCornelia Huck return r; 962d426d9fbSCornelia Huck } 963d426d9fbSCornelia Huck r = map_indicator(&dev->routes.adapter, dev->indicators); 964d426d9fbSCornelia Huck if (r) { 965d426d9fbSCornelia Huck return r; 966d426d9fbSCornelia Huck } 967d426d9fbSCornelia Huck dev->routes.adapter.summary_addr = dev->summary_indicator->map; 968d426d9fbSCornelia Huck dev->routes.adapter.ind_addr = dev->indicators->map; 969d426d9fbSCornelia Huck 970d426d9fbSCornelia Huck return 0; 971d426d9fbSCornelia Huck } 972d426d9fbSCornelia Huck 973d426d9fbSCornelia Huck static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs) 974d426d9fbSCornelia Huck { 975d426d9fbSCornelia Huck int i; 976d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 977d426d9fbSCornelia Huck int ret; 978d426d9fbSCornelia Huck S390FLICState *fs = s390_get_flic(); 9796762808fSDavid Hildenbrand S390FLICStateClass *fsc = s390_get_flic_class(fs); 980d426d9fbSCornelia Huck 981d426d9fbSCornelia Huck ret = virtio_ccw_get_mappings(dev); 982d426d9fbSCornelia Huck if (ret) { 983d426d9fbSCornelia Huck return ret; 984d426d9fbSCornelia Huck } 985d426d9fbSCornelia Huck for (i = 0; i < nvqs; i++) { 986d426d9fbSCornelia Huck if (!virtio_queue_get_num(vdev, i)) { 987d426d9fbSCornelia Huck break; 988d426d9fbSCornelia Huck } 989d426d9fbSCornelia Huck } 990d426d9fbSCornelia Huck dev->routes.num_routes = i; 991d426d9fbSCornelia Huck return fsc->add_adapter_routes(fs, &dev->routes); 992d426d9fbSCornelia Huck } 993d426d9fbSCornelia Huck 994d426d9fbSCornelia Huck static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs) 995d426d9fbSCornelia Huck { 996d426d9fbSCornelia Huck S390FLICState *fs = s390_get_flic(); 9976762808fSDavid Hildenbrand S390FLICStateClass *fsc = s390_get_flic_class(fs); 998d426d9fbSCornelia Huck 999d426d9fbSCornelia Huck fsc->release_adapter_routes(fs, &dev->routes); 1000d426d9fbSCornelia Huck } 1001d426d9fbSCornelia Huck 1002d426d9fbSCornelia Huck static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n) 1003d426d9fbSCornelia Huck { 1004d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1005d426d9fbSCornelia Huck VirtQueue *vq = virtio_get_queue(vdev, n); 1006d426d9fbSCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); 1007d426d9fbSCornelia Huck 10081c9b71a7SEric Auger return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, notifier, NULL, 1009d426d9fbSCornelia Huck dev->routes.gsi[n]); 1010d426d9fbSCornelia Huck } 1011d426d9fbSCornelia Huck 1012d426d9fbSCornelia Huck static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n) 1013d426d9fbSCornelia Huck { 1014d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1015d426d9fbSCornelia Huck VirtQueue *vq = virtio_get_queue(vdev, n); 1016d426d9fbSCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); 1017d426d9fbSCornelia Huck int ret; 1018d426d9fbSCornelia Huck 10191c9b71a7SEric Auger ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, notifier, 1020d426d9fbSCornelia Huck dev->routes.gsi[n]); 1021d426d9fbSCornelia Huck assert(ret == 0); 1022d426d9fbSCornelia Huck } 1023d426d9fbSCornelia Huck 1024320ce850SCornelia Huck static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, 1025320ce850SCornelia Huck bool assign, bool with_irqfd) 1026320ce850SCornelia Huck { 1027f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1028f24a6840SPaolo Bonzini VirtQueue *vq = virtio_get_queue(vdev, n); 1029320ce850SCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); 1030f24a6840SPaolo Bonzini VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); 1031320ce850SCornelia Huck 1032320ce850SCornelia Huck if (assign) { 1033320ce850SCornelia Huck int r = event_notifier_init(notifier, 0); 1034320ce850SCornelia Huck 1035320ce850SCornelia Huck if (r < 0) { 1036320ce850SCornelia Huck return r; 1037320ce850SCornelia Huck } 1038320ce850SCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); 1039d426d9fbSCornelia Huck if (with_irqfd) { 1040d426d9fbSCornelia Huck r = virtio_ccw_add_irqfd(dev, n); 1041d426d9fbSCornelia Huck if (r) { 1042d426d9fbSCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, false, 1043d426d9fbSCornelia Huck with_irqfd); 1044d426d9fbSCornelia Huck return r; 1045d426d9fbSCornelia Huck } 1046d426d9fbSCornelia Huck } 1047d426d9fbSCornelia Huck /* 1048d426d9fbSCornelia Huck * We do not support individual masking for channel devices, so we 1049d426d9fbSCornelia Huck * need to manually trigger any guest masking callbacks here. 1050320ce850SCornelia Huck */ 10512858bc68SWei Huang if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) { 1052f24a6840SPaolo Bonzini k->guest_notifier_mask(vdev, n, false); 1053320ce850SCornelia Huck } 1054320ce850SCornelia Huck /* get lost events and re-inject */ 1055320ce850SCornelia Huck if (k->guest_notifier_pending && 1056f24a6840SPaolo Bonzini k->guest_notifier_pending(vdev, n)) { 1057320ce850SCornelia Huck event_notifier_set(notifier); 1058320ce850SCornelia Huck } 1059320ce850SCornelia Huck } else { 10602858bc68SWei Huang if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) { 1061f24a6840SPaolo Bonzini k->guest_notifier_mask(vdev, n, true); 1062320ce850SCornelia Huck } 1063d426d9fbSCornelia Huck if (with_irqfd) { 1064d426d9fbSCornelia Huck virtio_ccw_remove_irqfd(dev, n); 1065d426d9fbSCornelia Huck } 1066320ce850SCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); 1067320ce850SCornelia Huck event_notifier_cleanup(notifier); 1068320ce850SCornelia Huck } 1069320ce850SCornelia Huck return 0; 1070320ce850SCornelia Huck } 1071320ce850SCornelia Huck 1072320ce850SCornelia Huck static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs, 1073320ce850SCornelia Huck bool assigned) 1074320ce850SCornelia Huck { 1075320ce850SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1076f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1077b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(d); 1078b804e8a6SJing Liu bool with_irqfd = ccw_dev->sch->thinint_active && kvm_irqfds_enabled(); 1079320ce850SCornelia Huck int r, n; 1080320ce850SCornelia Huck 1081d426d9fbSCornelia Huck if (with_irqfd && assigned) { 1082d426d9fbSCornelia Huck /* irq routes need to be set up before assigning irqfds */ 1083d426d9fbSCornelia Huck r = virtio_ccw_setup_irqroutes(dev, nvqs); 1084d426d9fbSCornelia Huck if (r < 0) { 1085d426d9fbSCornelia Huck goto irqroute_error; 1086d426d9fbSCornelia Huck } 1087d426d9fbSCornelia Huck } 1088320ce850SCornelia Huck for (n = 0; n < nvqs; n++) { 1089320ce850SCornelia Huck if (!virtio_queue_get_num(vdev, n)) { 1090320ce850SCornelia Huck break; 1091320ce850SCornelia Huck } 1092d426d9fbSCornelia Huck r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd); 1093320ce850SCornelia Huck if (r < 0) { 1094320ce850SCornelia Huck goto assign_error; 1095320ce850SCornelia Huck } 1096320ce850SCornelia Huck } 1097d426d9fbSCornelia Huck if (with_irqfd && !assigned) { 1098d426d9fbSCornelia Huck /* release irq routes after irqfds have been released */ 1099d426d9fbSCornelia Huck virtio_ccw_release_irqroutes(dev, nvqs); 1100d426d9fbSCornelia Huck } 1101320ce850SCornelia Huck return 0; 1102320ce850SCornelia Huck 1103320ce850SCornelia Huck assign_error: 1104320ce850SCornelia Huck while (--n >= 0) { 1105320ce850SCornelia Huck virtio_ccw_set_guest_notifier(dev, n, !assigned, false); 1106320ce850SCornelia Huck } 1107d426d9fbSCornelia Huck irqroute_error: 1108d426d9fbSCornelia Huck if (with_irqfd && assigned) { 1109d426d9fbSCornelia Huck virtio_ccw_release_irqroutes(dev, nvqs); 1110d426d9fbSCornelia Huck } 1111320ce850SCornelia Huck return r; 1112320ce850SCornelia Huck } 1113320ce850SCornelia Huck 1114bcb2b582SJens Freimann static void virtio_ccw_save_queue(DeviceState *d, int n, QEMUFile *f) 1115bcb2b582SJens Freimann { 1116bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1117bcb2b582SJens Freimann VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1118bcb2b582SJens Freimann 1119bcb2b582SJens Freimann qemu_put_be16(f, virtio_queue_vector(vdev, n)); 1120bcb2b582SJens Freimann } 1121bcb2b582SJens Freimann 1122bcb2b582SJens Freimann static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f) 1123bcb2b582SJens Freimann { 1124bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1125bcb2b582SJens Freimann VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1126bcb2b582SJens Freimann uint16_t vector; 1127bcb2b582SJens Freimann 1128bcb2b582SJens Freimann qemu_get_be16s(f, &vector); 1129bcb2b582SJens Freimann virtio_queue_set_vector(vdev, n , vector); 1130bcb2b582SJens Freimann 1131bcb2b582SJens Freimann return 0; 1132bcb2b582SJens Freimann } 1133bcb2b582SJens Freimann 1134bcb2b582SJens Freimann static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) 1135bcb2b582SJens Freimann { 1136bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1137517ff12cSHalil Pasic vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL); 1138bcb2b582SJens Freimann } 1139bcb2b582SJens Freimann 1140bcb2b582SJens Freimann static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) 1141bcb2b582SJens Freimann { 1142bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1143517ff12cSHalil Pasic return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1); 1144bcb2b582SJens Freimann } 1145bcb2b582SJens Freimann 1146d1b4259fSMaxime Coquelin static void virtio_ccw_pre_plugged(DeviceState *d, Error **errp) 1147d1b4259fSMaxime Coquelin { 1148d1b4259fSMaxime Coquelin VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1149d1b4259fSMaxime Coquelin VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1150d1b4259fSMaxime Coquelin 1151d1b4259fSMaxime Coquelin if (dev->max_rev >= 1) { 1152d1b4259fSMaxime Coquelin virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1); 1153d1b4259fSMaxime Coquelin } 1154d1b4259fSMaxime Coquelin } 1155d1b4259fSMaxime Coquelin 1156fb846a09SCornelia Huck /* This is called by virtio-bus just after the device is plugged. */ 1157e8398045SJason Wang static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) 1158fb846a09SCornelia Huck { 1159fb846a09SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 116010ceaa1eSJason Wang VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1161b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(d); 1162b804e8a6SJing Liu SubchDev *sch = ccw_dev->sch; 116310ceaa1eSJason Wang int n = virtio_get_num_queues(vdev); 11640708afa7SHalil Pasic S390FLICState *flic = s390_get_flic(); 116510ceaa1eSJason Wang 1166d1b4259fSMaxime Coquelin if (!virtio_has_feature(vdev->host_features, VIRTIO_F_VERSION_1)) { 1167d1b4259fSMaxime Coquelin dev->max_rev = 0; 1168d1b4259fSMaxime Coquelin } 1169d1b4259fSMaxime Coquelin 11709b3a35ecSCornelia Huck if (!virtio_ccw_rev_max(dev) && !virtio_legacy_allowed(vdev)) { 1171d55f5182SStefano Garzarella /* 1172d55f5182SStefano Garzarella * To avoid migration issues, we allow legacy mode when legacy 1173d55f5182SStefano Garzarella * check is disabled in the old machine types (< 5.1). 1174d55f5182SStefano Garzarella */ 1175d55f5182SStefano Garzarella if (virtio_legacy_check_disabled(vdev)) { 1176d55f5182SStefano Garzarella warn_report("device requires revision >= 1, but for backward " 1177d55f5182SStefano Garzarella "compatibility max_revision=0 is allowed"); 1178d55f5182SStefano Garzarella } else { 11799b3a35ecSCornelia Huck error_setg(errp, "Invalid value of property max_rev " 11809b3a35ecSCornelia Huck "(is %d expected >= 1)", virtio_ccw_rev_max(dev)); 11819b3a35ecSCornelia Huck return; 11829b3a35ecSCornelia Huck } 1183d55f5182SStefano Garzarella } 11849b3a35ecSCornelia Huck 1185b1914b82SHalil Pasic if (virtio_get_num_queues(vdev) > VIRTIO_QUEUE_MAX) { 1186b34aee54SMichael Tokarev error_setg(errp, "The number of virtqueues %d " 1187b1914b82SHalil Pasic "exceeds virtio limit %d", n, 1188b1914b82SHalil Pasic VIRTIO_QUEUE_MAX); 118910ceaa1eSJason Wang return; 119010ceaa1eSJason Wang } 11910708afa7SHalil Pasic if (virtio_get_num_queues(vdev) > flic->adapter_routes_max_batch) { 11920708afa7SHalil Pasic error_setg(errp, "The number of virtqueues %d " 11930708afa7SHalil Pasic "exceeds flic adapter route limit %d", n, 11940708afa7SHalil Pasic flic->adapter_routes_max_batch); 11950708afa7SHalil Pasic return; 11960708afa7SHalil Pasic } 1197fb846a09SCornelia Huck 1198fb846a09SCornelia Huck sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus); 1199fb846a09SCornelia Huck 1200542571d5SCornelia Huck 1201fb846a09SCornelia Huck css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1202fb846a09SCornelia Huck d->hotplugged, 1); 1203fb846a09SCornelia Huck } 1204fb846a09SCornelia Huck 1205fb846a09SCornelia Huck static void virtio_ccw_device_unplugged(DeviceState *d) 1206fb846a09SCornelia Huck { 1207fb846a09SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1208fb846a09SCornelia Huck 1209fb846a09SCornelia Huck virtio_ccw_stop_ioeventfd(dev); 1210fb846a09SCornelia Huck } 1211a5cf2bb4SCornelia Huck /**************** Virtio-ccw Bus Device Descriptions *******************/ 1212a5cf2bb4SCornelia Huck 12135e5ced38SMarkus Armbruster static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp) 1214a5cf2bb4SCornelia Huck { 1215a5cf2bb4SCornelia Huck VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; 1216a5cf2bb4SCornelia Huck 12171bf4d7aaSAndreas Färber virtio_ccw_bus_new(&_dev->bus, sizeof(_dev->bus), _dev); 12181fa75523SCornelia Huck virtio_ccw_device_realize(_dev, errp); 1219a5cf2bb4SCornelia Huck } 1220a5cf2bb4SCornelia Huck 1221b69c3c21SMarkus Armbruster static void virtio_ccw_busdev_unrealize(DeviceState *dev) 1222a5cf2bb4SCornelia Huck { 1223a5cf2bb4SCornelia Huck VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; 1224a5cf2bb4SCornelia Huck 1225b69c3c21SMarkus Armbruster virtio_ccw_device_unrealize(_dev); 1226a5cf2bb4SCornelia Huck } 1227a5cf2bb4SCornelia Huck 1228b804e8a6SJing Liu static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, 1229277bc95eSIgor Mammedov DeviceState *dev, Error **errp) 1230a5cf2bb4SCornelia Huck { 1231b804e8a6SJing Liu VirtioCcwDevice *_dev = to_virtio_ccw_dev_fast(dev); 1232a5cf2bb4SCornelia Huck 12330b81c1efSPaolo Bonzini virtio_ccw_stop_ioeventfd(_dev); 1234a5cf2bb4SCornelia Huck } 1235a5cf2bb4SCornelia Huck 1236a5cf2bb4SCornelia Huck static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) 1237a5cf2bb4SCornelia Huck { 1238a5cf2bb4SCornelia Huck DeviceClass *dc = DEVICE_CLASS(klass); 1239b804e8a6SJing Liu CCWDeviceClass *k = CCW_DEVICE_CLASS(dc); 1240838fb84fSCornelia Huck VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_CLASS(klass); 1241a5cf2bb4SCornelia Huck 1242b804e8a6SJing Liu k->unplug = virtio_ccw_busdev_unplug; 12435e5ced38SMarkus Armbruster dc->realize = virtio_ccw_busdev_realize; 124424118af8SNia Alarie dc->unrealize = virtio_ccw_busdev_unrealize; 1245838fb84fSCornelia Huck device_class_set_parent_reset(dc, virtio_ccw_reset, &vdc->parent_reset); 1246a5cf2bb4SCornelia Huck } 1247a5cf2bb4SCornelia Huck 1248a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_device_info = { 1249a5cf2bb4SCornelia Huck .name = TYPE_VIRTIO_CCW_DEVICE, 1250b804e8a6SJing Liu .parent = TYPE_CCW_DEVICE, 1251a5cf2bb4SCornelia Huck .instance_size = sizeof(VirtioCcwDevice), 1252a5cf2bb4SCornelia Huck .class_init = virtio_ccw_device_class_init, 1253a5cf2bb4SCornelia Huck .class_size = sizeof(VirtIOCCWDeviceClass), 1254a5cf2bb4SCornelia Huck .abstract = true, 1255a5cf2bb4SCornelia Huck }; 1256a5cf2bb4SCornelia Huck 1257a5cf2bb4SCornelia Huck /* virtio-ccw-bus */ 1258a5cf2bb4SCornelia Huck 12591bf4d7aaSAndreas Färber static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, 12601bf4d7aaSAndreas Färber VirtioCcwDevice *dev) 1261a5cf2bb4SCornelia Huck { 1262a5cf2bb4SCornelia Huck DeviceState *qdev = DEVICE(dev); 1263f4dd69aaSKONRAD Frederic char virtio_bus_name[] = "virtio-bus"; 1264a5cf2bb4SCornelia Huck 1265d637e1dcSPeter Maydell qbus_init(bus, bus_size, TYPE_VIRTIO_CCW_BUS, qdev, virtio_bus_name); 1266a5cf2bb4SCornelia Huck } 1267a5cf2bb4SCornelia Huck 1268a5cf2bb4SCornelia Huck static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) 1269a5cf2bb4SCornelia Huck { 1270a5cf2bb4SCornelia Huck VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); 1271a5cf2bb4SCornelia Huck BusClass *bus_class = BUS_CLASS(klass); 1272a5cf2bb4SCornelia Huck 1273a5cf2bb4SCornelia Huck bus_class->max_dev = 1; 1274a5cf2bb4SCornelia Huck k->notify = virtio_ccw_notify; 1275b4436a0bSCornelia Huck k->vmstate_change = virtio_ccw_vmstate_change; 1276320ce850SCornelia Huck k->query_guest_notifiers = virtio_ccw_query_guest_notifiers; 1277320ce850SCornelia Huck k->set_guest_notifiers = virtio_ccw_set_guest_notifiers; 1278bcb2b582SJens Freimann k->save_queue = virtio_ccw_save_queue; 1279bcb2b582SJens Freimann k->load_queue = virtio_ccw_load_queue; 1280bcb2b582SJens Freimann k->save_config = virtio_ccw_save_config; 1281bcb2b582SJens Freimann k->load_config = virtio_ccw_load_config; 1282d1b4259fSMaxime Coquelin k->pre_plugged = virtio_ccw_pre_plugged; 1283fb846a09SCornelia Huck k->device_plugged = virtio_ccw_device_plugged; 1284fb846a09SCornelia Huck k->device_unplugged = virtio_ccw_device_unplugged; 12858e93cef1SPaolo Bonzini k->ioeventfd_enabled = virtio_ccw_ioeventfd_enabled; 12867c55f68aSCornelia Huck k->ioeventfd_assign = virtio_ccw_ioeventfd_assign; 1287a5cf2bb4SCornelia Huck } 1288a5cf2bb4SCornelia Huck 1289a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_bus_info = { 1290a5cf2bb4SCornelia Huck .name = TYPE_VIRTIO_CCW_BUS, 1291a5cf2bb4SCornelia Huck .parent = TYPE_VIRTIO_BUS, 1292a5cf2bb4SCornelia Huck .instance_size = sizeof(VirtioCcwBusState), 129374ded8b4SCornelia Huck .class_size = sizeof(VirtioCcwBusClass), 1294a5cf2bb4SCornelia Huck .class_init = virtio_ccw_bus_class_init, 1295a5cf2bb4SCornelia Huck }; 1296a5cf2bb4SCornelia Huck 1297a5cf2bb4SCornelia Huck static void virtio_ccw_register(void) 1298a5cf2bb4SCornelia Huck { 1299a5cf2bb4SCornelia Huck type_register_static(&virtio_ccw_bus_info); 1300a5cf2bb4SCornelia Huck type_register_static(&virtio_ccw_device_info); 1301a5cf2bb4SCornelia Huck } 1302a5cf2bb4SCornelia Huck 1303a5cf2bb4SCornelia Huck type_init(virtio_ccw_register) 1304