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" 15bd3f16acSPaolo Bonzini #include "sysemu/kvm.h" 16a5cf2bb4SCornelia Huck #include "net/net.h" 170d09e41aSPaolo Bonzini #include "hw/virtio/virtio.h" 18ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h" 190d09e41aSPaolo Bonzini #include "hw/virtio/virtio-net.h" 20a5cf2bb4SCornelia Huck #include "hw/sysbus.h" 21a5cf2bb4SCornelia Huck #include "qemu/bitops.h" 22d49b6836SMarkus Armbruster #include "qemu/error-report.h" 230b8fa32fSMarkus Armbruster #include "qemu/module.h" 24c42767f2SThomas Huth #include "hw/virtio/virtio-access.h" 250d09e41aSPaolo Bonzini #include "hw/virtio/virtio-bus.h" 26d426d9fbSCornelia Huck #include "hw/s390x/adapter.h" 27d426d9fbSCornelia Huck #include "hw/s390x/s390_flic.h" 28a5cf2bb4SCornelia Huck 29bd3f16acSPaolo Bonzini #include "hw/s390x/ioinst.h" 30bd3f16acSPaolo Bonzini #include "hw/s390x/css.h" 31a5cf2bb4SCornelia Huck #include "virtio-ccw.h" 32a5cf2bb4SCornelia Huck #include "trace.h" 33dd70bd0dSJing Liu #include "hw/s390x/css-bridge.h" 34517ff12cSHalil Pasic #include "hw/s390x/s390-virtio-ccw.h" 35a5cf2bb4SCornelia Huck 36797b6086SHalil Pasic #define NR_CLASSIC_INDICATOR_BITS 64 37797b6086SHalil Pasic 38517ff12cSHalil Pasic static int virtio_ccw_dev_post_load(void *opaque, int version_id) 39517ff12cSHalil Pasic { 40517ff12cSHalil Pasic VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(opaque); 41517ff12cSHalil Pasic CcwDevice *ccw_dev = CCW_DEVICE(dev); 42517ff12cSHalil Pasic CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); 43517ff12cSHalil Pasic 44517ff12cSHalil Pasic ccw_dev->sch->driver_data = dev; 45517ff12cSHalil Pasic if (ccw_dev->sch->thinint_active) { 46517ff12cSHalil Pasic dev->routes.adapter.adapter_id = css_get_adapter_id( 47517ff12cSHalil Pasic CSS_IO_ADAPTER_VIRTIO, 48517ff12cSHalil Pasic dev->thinint_isc); 49517ff12cSHalil Pasic } 50517ff12cSHalil Pasic /* Re-fill subch_id after loading the subchannel states.*/ 51517ff12cSHalil Pasic if (ck->refill_ids) { 52517ff12cSHalil Pasic ck->refill_ids(ccw_dev); 53517ff12cSHalil Pasic } 54517ff12cSHalil Pasic return 0; 55517ff12cSHalil Pasic } 56517ff12cSHalil Pasic 57517ff12cSHalil Pasic typedef struct VirtioCcwDeviceTmp { 58517ff12cSHalil Pasic VirtioCcwDevice *parent; 59517ff12cSHalil Pasic uint16_t config_vector; 60517ff12cSHalil Pasic } VirtioCcwDeviceTmp; 61517ff12cSHalil Pasic 6244b1ff31SDr. David Alan Gilbert static int virtio_ccw_dev_tmp_pre_save(void *opaque) 63517ff12cSHalil Pasic { 64517ff12cSHalil Pasic VirtioCcwDeviceTmp *tmp = opaque; 65517ff12cSHalil Pasic VirtioCcwDevice *dev = tmp->parent; 66517ff12cSHalil Pasic VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 67517ff12cSHalil Pasic 68517ff12cSHalil Pasic tmp->config_vector = vdev->config_vector; 6944b1ff31SDr. David Alan Gilbert 7044b1ff31SDr. David Alan Gilbert return 0; 71517ff12cSHalil Pasic } 72517ff12cSHalil Pasic 73517ff12cSHalil Pasic static int virtio_ccw_dev_tmp_post_load(void *opaque, int version_id) 74517ff12cSHalil Pasic { 75517ff12cSHalil Pasic VirtioCcwDeviceTmp *tmp = opaque; 76517ff12cSHalil Pasic VirtioCcwDevice *dev = tmp->parent; 77517ff12cSHalil Pasic VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 78517ff12cSHalil Pasic 79517ff12cSHalil Pasic vdev->config_vector = tmp->config_vector; 80517ff12cSHalil Pasic return 0; 81517ff12cSHalil Pasic } 82517ff12cSHalil Pasic 83517ff12cSHalil Pasic const VMStateDescription vmstate_virtio_ccw_dev_tmp = { 84517ff12cSHalil Pasic .name = "s390_virtio_ccw_dev_tmp", 85517ff12cSHalil Pasic .pre_save = virtio_ccw_dev_tmp_pre_save, 86517ff12cSHalil Pasic .post_load = virtio_ccw_dev_tmp_post_load, 87517ff12cSHalil Pasic .fields = (VMStateField[]) { 88517ff12cSHalil Pasic VMSTATE_UINT16(config_vector, VirtioCcwDeviceTmp), 89517ff12cSHalil Pasic VMSTATE_END_OF_LIST() 90517ff12cSHalil Pasic } 91517ff12cSHalil Pasic }; 92517ff12cSHalil Pasic 93517ff12cSHalil Pasic const VMStateDescription vmstate_virtio_ccw_dev = { 94517ff12cSHalil Pasic .name = "s390_virtio_ccw_dev", 95517ff12cSHalil Pasic .version_id = 1, 96517ff12cSHalil Pasic .minimum_version_id = 1, 97517ff12cSHalil Pasic .post_load = virtio_ccw_dev_post_load, 98517ff12cSHalil Pasic .fields = (VMStateField[]) { 99517ff12cSHalil Pasic VMSTATE_CCW_DEVICE(parent_obj, VirtioCcwDevice), 100517ff12cSHalil Pasic VMSTATE_PTR_TO_IND_ADDR(indicators, VirtioCcwDevice), 101517ff12cSHalil Pasic VMSTATE_PTR_TO_IND_ADDR(indicators2, VirtioCcwDevice), 102517ff12cSHalil Pasic VMSTATE_PTR_TO_IND_ADDR(summary_indicator, VirtioCcwDevice), 103517ff12cSHalil Pasic /* 104517ff12cSHalil Pasic * Ugly hack because VirtIODevice does not migrate itself. 105517ff12cSHalil Pasic * This also makes legacy via vmstate_save_state possible. 106517ff12cSHalil Pasic */ 107517ff12cSHalil Pasic VMSTATE_WITH_TMP(VirtioCcwDevice, VirtioCcwDeviceTmp, 108517ff12cSHalil Pasic vmstate_virtio_ccw_dev_tmp), 109517ff12cSHalil Pasic VMSTATE_STRUCT(routes, VirtioCcwDevice, 1, vmstate_adapter_routes, 110517ff12cSHalil Pasic AdapterRoutes), 111517ff12cSHalil Pasic VMSTATE_UINT8(thinint_isc, VirtioCcwDevice), 112517ff12cSHalil Pasic VMSTATE_INT32(revision, VirtioCcwDevice), 113517ff12cSHalil Pasic VMSTATE_END_OF_LIST() 114517ff12cSHalil Pasic } 115517ff12cSHalil Pasic }; 116517ff12cSHalil Pasic 1171bf4d7aaSAndreas Färber static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, 1181bf4d7aaSAndreas Färber VirtioCcwDevice *dev); 119d51fcfacSKONRAD Frederic 120a5cf2bb4SCornelia Huck VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) 121a5cf2bb4SCornelia Huck { 122a5cf2bb4SCornelia Huck VirtIODevice *vdev = NULL; 123f24a6840SPaolo Bonzini VirtioCcwDevice *dev = sch->driver_data; 124a5cf2bb4SCornelia Huck 125f24a6840SPaolo Bonzini if (dev) { 126f24a6840SPaolo Bonzini vdev = virtio_bus_get_device(&dev->bus); 127a5cf2bb4SCornelia Huck } 128a5cf2bb4SCornelia Huck return vdev; 129a5cf2bb4SCornelia Huck } 130a5cf2bb4SCornelia Huck 131b4436a0bSCornelia Huck static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev) 132b4436a0bSCornelia Huck { 1337c55f68aSCornelia Huck virtio_bus_start_ioeventfd(&dev->bus); 134b4436a0bSCornelia Huck } 135b4436a0bSCornelia Huck 136b4436a0bSCornelia Huck static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev) 137b4436a0bSCornelia Huck { 1387c55f68aSCornelia Huck virtio_bus_stop_ioeventfd(&dev->bus); 1397c55f68aSCornelia Huck } 140b4436a0bSCornelia Huck 1418e93cef1SPaolo Bonzini static bool virtio_ccw_ioeventfd_enabled(DeviceState *d) 1427c55f68aSCornelia Huck { 1437c55f68aSCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1447c55f68aSCornelia Huck 1458e93cef1SPaolo Bonzini return (dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) != 0; 1467c55f68aSCornelia Huck } 1477c55f68aSCornelia Huck 1487c55f68aSCornelia Huck static int virtio_ccw_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, 1497c55f68aSCornelia Huck int n, bool assign) 1507c55f68aSCornelia Huck { 1517c55f68aSCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 152b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev); 153b804e8a6SJing Liu SubchDev *sch = ccw_dev->sch; 1547c55f68aSCornelia Huck uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid; 1557c55f68aSCornelia Huck 1567c55f68aSCornelia Huck return s390_assign_subch_ioeventfd(notifier, sch_id, n, assign); 157b4436a0bSCornelia Huck } 158b4436a0bSCornelia Huck 159a5cf2bb4SCornelia Huck /* Communication blocks used by several channel commands. */ 1600db87e0dSCornelia Huck typedef struct VqInfoBlockLegacy { 161a5cf2bb4SCornelia Huck uint64_t queue; 162a5cf2bb4SCornelia Huck uint32_t align; 163a5cf2bb4SCornelia Huck uint16_t index; 164a5cf2bb4SCornelia Huck uint16_t num; 1650db87e0dSCornelia Huck } QEMU_PACKED VqInfoBlockLegacy; 1660db87e0dSCornelia Huck 1670db87e0dSCornelia Huck typedef struct VqInfoBlock { 1680db87e0dSCornelia Huck uint64_t desc; 1690db87e0dSCornelia Huck uint32_t res0; 1700db87e0dSCornelia Huck uint16_t index; 1710db87e0dSCornelia Huck uint16_t num; 1720db87e0dSCornelia Huck uint64_t avail; 1730db87e0dSCornelia Huck uint64_t used; 174a5cf2bb4SCornelia Huck } QEMU_PACKED VqInfoBlock; 175a5cf2bb4SCornelia Huck 176a5cf2bb4SCornelia Huck typedef struct VqConfigBlock { 177a5cf2bb4SCornelia Huck uint16_t index; 178a5cf2bb4SCornelia Huck uint16_t num_max; 179a5cf2bb4SCornelia Huck } QEMU_PACKED VqConfigBlock; 180a5cf2bb4SCornelia Huck 181a5cf2bb4SCornelia Huck typedef struct VirtioFeatDesc { 182a5cf2bb4SCornelia Huck uint32_t features; 183a5cf2bb4SCornelia Huck uint8_t index; 184a5cf2bb4SCornelia Huck } QEMU_PACKED VirtioFeatDesc; 185a5cf2bb4SCornelia Huck 1867e749462SCornelia Huck typedef struct VirtioThinintInfo { 1877e749462SCornelia Huck hwaddr summary_indicator; 1887e749462SCornelia Huck hwaddr device_indicator; 1897e749462SCornelia Huck uint64_t ind_bit; 1907e749462SCornelia Huck uint8_t isc; 1917e749462SCornelia Huck } QEMU_PACKED VirtioThinintInfo; 1927e749462SCornelia Huck 193c42767f2SThomas Huth typedef struct VirtioRevInfo { 194c42767f2SThomas Huth uint16_t revision; 195c42767f2SThomas Huth uint16_t length; 196880a7817SPhilippe Mathieu-Daudé uint8_t data[]; 197c42767f2SThomas Huth } QEMU_PACKED VirtioRevInfo; 198c42767f2SThomas Huth 199a5cf2bb4SCornelia Huck /* Specify where the virtqueues for the subchannel are in guest memory. */ 2000db87e0dSCornelia Huck static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, 2010db87e0dSCornelia Huck VqInfoBlockLegacy *linfo) 202a5cf2bb4SCornelia Huck { 203f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_ccw_get_vdev(sch); 2040db87e0dSCornelia Huck uint16_t index = info ? info->index : linfo->index; 2050db87e0dSCornelia Huck uint16_t num = info ? info->num : linfo->num; 2060db87e0dSCornelia Huck uint64_t desc = info ? info->desc : linfo->queue; 207a5cf2bb4SCornelia Huck 208b1914b82SHalil Pasic if (index >= VIRTIO_QUEUE_MAX) { 209a5cf2bb4SCornelia Huck return -EINVAL; 210a5cf2bb4SCornelia Huck } 211a5cf2bb4SCornelia Huck 212a5cf2bb4SCornelia Huck /* Current code in virtio.c relies on 4K alignment. */ 2130db87e0dSCornelia Huck if (linfo && desc && (linfo->align != 4096)) { 214a5cf2bb4SCornelia Huck return -EINVAL; 215a5cf2bb4SCornelia Huck } 216a5cf2bb4SCornelia Huck 217f24a6840SPaolo Bonzini if (!vdev) { 218a5cf2bb4SCornelia Huck return -EINVAL; 219a5cf2bb4SCornelia Huck } 220a5cf2bb4SCornelia Huck 2210db87e0dSCornelia Huck if (info) { 2220db87e0dSCornelia Huck virtio_queue_set_rings(vdev, index, desc, info->avail, info->used); 2230db87e0dSCornelia Huck } else { 2240db87e0dSCornelia Huck virtio_queue_set_addr(vdev, index, desc); 2250db87e0dSCornelia Huck } 2260db87e0dSCornelia Huck if (!desc) { 227955cc8c9SJason Wang virtio_queue_set_vector(vdev, index, VIRTIO_NO_VECTOR); 228a5cf2bb4SCornelia Huck } else { 22979cd0c80SCornelia Huck if (info) { 23079cd0c80SCornelia Huck /* virtio-1 allows changing the ring size. */ 2318c797e75SMichael S. Tsirkin if (virtio_queue_get_max_num(vdev, index) < num) { 23279cd0c80SCornelia Huck /* Fail if we exceed the maximum number. */ 233a5cf2bb4SCornelia Huck return -EINVAL; 234a5cf2bb4SCornelia Huck } 23579cd0c80SCornelia Huck virtio_queue_set_num(vdev, index, num); 23679cd0c80SCornelia Huck } else if (virtio_queue_get_num(vdev, index) > num) { 23779cd0c80SCornelia Huck /* Fail if we don't have a big enough queue. */ 23879cd0c80SCornelia Huck return -EINVAL; 23979cd0c80SCornelia Huck } 24079cd0c80SCornelia Huck /* We ignore possible increased num for legacy for compatibility. */ 241f24a6840SPaolo Bonzini virtio_queue_set_vector(vdev, index, index); 242a5cf2bb4SCornelia Huck } 243a5cf2bb4SCornelia Huck /* tell notify handler in case of config change */ 244b1914b82SHalil Pasic vdev->config_vector = VIRTIO_QUEUE_MAX; 245a5cf2bb4SCornelia Huck return 0; 246a5cf2bb4SCornelia Huck } 247a5cf2bb4SCornelia Huck 248fa8b0ca5SCornelia Huck static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) 249fa8b0ca5SCornelia Huck { 250b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev); 251b804e8a6SJing Liu 252fa8b0ca5SCornelia Huck virtio_ccw_stop_ioeventfd(dev); 253fa8b0ca5SCornelia Huck virtio_reset(vdev); 254fa8b0ca5SCornelia Huck if (dev->indicators) { 255fa8b0ca5SCornelia Huck release_indicator(&dev->routes.adapter, dev->indicators); 256fa8b0ca5SCornelia Huck dev->indicators = NULL; 257fa8b0ca5SCornelia Huck } 258fa8b0ca5SCornelia Huck if (dev->indicators2) { 259fa8b0ca5SCornelia Huck release_indicator(&dev->routes.adapter, dev->indicators2); 260fa8b0ca5SCornelia Huck dev->indicators2 = NULL; 261fa8b0ca5SCornelia Huck } 262fa8b0ca5SCornelia Huck if (dev->summary_indicator) { 263fa8b0ca5SCornelia Huck release_indicator(&dev->routes.adapter, dev->summary_indicator); 264fa8b0ca5SCornelia Huck dev->summary_indicator = NULL; 265fa8b0ca5SCornelia Huck } 266b804e8a6SJing Liu ccw_dev->sch->thinint_active = false; 267fa8b0ca5SCornelia Huck } 268fa8b0ca5SCornelia Huck 2690db87e0dSCornelia Huck static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len, 2700db87e0dSCornelia Huck bool is_legacy) 271a5cf2bb4SCornelia Huck { 272a5cf2bb4SCornelia Huck int ret; 273a5cf2bb4SCornelia Huck VqInfoBlock info; 2740db87e0dSCornelia Huck VqInfoBlockLegacy linfo; 2750db87e0dSCornelia Huck size_t info_len = is_legacy ? sizeof(linfo) : sizeof(info); 2760db87e0dSCornelia Huck 2770db87e0dSCornelia Huck if (check_len) { 2780db87e0dSCornelia Huck if (ccw.count != info_len) { 2790db87e0dSCornelia Huck return -EINVAL; 2800db87e0dSCornelia Huck } 2810db87e0dSCornelia Huck } else if (ccw.count < info_len) { 2820db87e0dSCornelia Huck /* Can't execute command. */ 2830db87e0dSCornelia Huck return -EINVAL; 2840db87e0dSCornelia Huck } 2850db87e0dSCornelia Huck if (!ccw.cda) { 2860db87e0dSCornelia Huck return -EFAULT; 2870db87e0dSCornelia Huck } 2880db87e0dSCornelia Huck if (is_legacy) { 289f57ba058SHalil Pasic ccw_dstream_read(&sch->cds, linfo); 290c9aacaadSPeter Maydell linfo.queue = be64_to_cpu(linfo.queue); 291c9aacaadSPeter Maydell linfo.align = be32_to_cpu(linfo.align); 292c9aacaadSPeter Maydell linfo.index = be16_to_cpu(linfo.index); 293c9aacaadSPeter Maydell linfo.num = be16_to_cpu(linfo.num); 2940db87e0dSCornelia Huck ret = virtio_ccw_set_vqs(sch, NULL, &linfo); 2950db87e0dSCornelia Huck } else { 296f57ba058SHalil Pasic ccw_dstream_read(&sch->cds, info); 297c9aacaadSPeter Maydell info.desc = be64_to_cpu(info.desc); 298c9aacaadSPeter Maydell info.index = be16_to_cpu(info.index); 299c9aacaadSPeter Maydell info.num = be16_to_cpu(info.num); 300c9aacaadSPeter Maydell info.avail = be64_to_cpu(info.avail); 301c9aacaadSPeter Maydell info.used = be64_to_cpu(info.used); 3020db87e0dSCornelia Huck ret = virtio_ccw_set_vqs(sch, &info, NULL); 3030db87e0dSCornelia Huck } 3040db87e0dSCornelia Huck sch->curr_status.scsw.count = 0; 3050db87e0dSCornelia Huck return ret; 3060db87e0dSCornelia Huck } 3070db87e0dSCornelia Huck 3080db87e0dSCornelia Huck static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) 3090db87e0dSCornelia Huck { 3100db87e0dSCornelia Huck int ret; 311c42767f2SThomas Huth VirtioRevInfo revinfo; 312a5cf2bb4SCornelia Huck uint8_t status; 313a5cf2bb4SCornelia Huck VirtioFeatDesc features; 314a5cf2bb4SCornelia Huck hwaddr indicators; 315a5cf2bb4SCornelia Huck VqConfigBlock vq_config; 316a5cf2bb4SCornelia Huck VirtioCcwDevice *dev = sch->driver_data; 317f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_ccw_get_vdev(sch); 318a5cf2bb4SCornelia Huck bool check_len; 319a5cf2bb4SCornelia Huck int len; 320f57ba058SHalil Pasic VirtioThinintInfo thinint; 321a5cf2bb4SCornelia Huck 322a5cf2bb4SCornelia Huck if (!dev) { 323a5cf2bb4SCornelia Huck return -EINVAL; 324a5cf2bb4SCornelia Huck } 325a5cf2bb4SCornelia Huck 326a5cf2bb4SCornelia Huck trace_virtio_ccw_interpret_ccw(sch->cssid, sch->ssid, sch->schid, 327a5cf2bb4SCornelia Huck ccw.cmd_code); 328a5cf2bb4SCornelia Huck check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); 329a5cf2bb4SCornelia Huck 33047e13dfdSHalil Pasic if (dev->force_revision_1 && dev->revision < 0 && 33147e13dfdSHalil Pasic ccw.cmd_code != CCW_CMD_SET_VIRTIO_REV) { 33247e13dfdSHalil Pasic /* 33347e13dfdSHalil Pasic * virtio-1 drivers must start with negotiating to a revision >= 1, 33447e13dfdSHalil Pasic * so post a command reject for all other commands 33547e13dfdSHalil Pasic */ 33647e13dfdSHalil Pasic return -ENOSYS; 33747e13dfdSHalil Pasic } 33847e13dfdSHalil Pasic 339a5cf2bb4SCornelia Huck /* Look at the command. */ 340a5cf2bb4SCornelia Huck switch (ccw.cmd_code) { 341a5cf2bb4SCornelia Huck case CCW_CMD_SET_VQ: 3420db87e0dSCornelia Huck ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1); 343a5cf2bb4SCornelia Huck break; 344a5cf2bb4SCornelia Huck case CCW_CMD_VDEV_RESET: 345fa8b0ca5SCornelia Huck virtio_ccw_reset_virtio(dev, vdev); 346a5cf2bb4SCornelia Huck ret = 0; 347a5cf2bb4SCornelia Huck break; 348a5cf2bb4SCornelia Huck case CCW_CMD_READ_FEAT: 349a5cf2bb4SCornelia Huck if (check_len) { 350a5cf2bb4SCornelia Huck if (ccw.count != sizeof(features)) { 351a5cf2bb4SCornelia Huck ret = -EINVAL; 352a5cf2bb4SCornelia Huck break; 353a5cf2bb4SCornelia Huck } 354a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(features)) { 355a5cf2bb4SCornelia Huck /* Can't execute command. */ 356a5cf2bb4SCornelia Huck ret = -EINVAL; 357a5cf2bb4SCornelia Huck break; 358a5cf2bb4SCornelia Huck } 359a5cf2bb4SCornelia Huck if (!ccw.cda) { 360a5cf2bb4SCornelia Huck ret = -EFAULT; 361a5cf2bb4SCornelia Huck } else { 3629b706dbbSMichael S. Tsirkin VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); 3639b706dbbSMichael S. Tsirkin 364f57ba058SHalil Pasic ccw_dstream_advance(&sch->cds, sizeof(features.features)); 365f57ba058SHalil Pasic ccw_dstream_read(&sch->cds, features.index); 3666b8f1020SCornelia Huck if (features.index == 0) { 367542571d5SCornelia Huck if (dev->revision >= 1) { 368542571d5SCornelia Huck /* Don't offer legacy features for modern devices. */ 369542571d5SCornelia Huck features.features = (uint32_t) 3709b706dbbSMichael S. Tsirkin (vdev->host_features & ~vdc->legacy_features); 371542571d5SCornelia Huck } else { 372c42767f2SThomas Huth features.features = (uint32_t)vdev->host_features; 373542571d5SCornelia Huck } 374b4f8f9dfSCornelia Huck } else if ((features.index == 1) && (dev->revision >= 1)) { 375c42767f2SThomas Huth /* 376b4f8f9dfSCornelia Huck * Only offer feature bits beyond 31 if the guest has 377b4f8f9dfSCornelia Huck * negotiated at least revision 1. 378c42767f2SThomas Huth */ 379b4f8f9dfSCornelia Huck features.features = (uint32_t)(vdev->host_features >> 32); 380a5cf2bb4SCornelia Huck } else { 381a5cf2bb4SCornelia Huck /* Return zeroes if the guest supports more feature bits. */ 382a5cf2bb4SCornelia Huck features.features = 0; 383a5cf2bb4SCornelia Huck } 384f57ba058SHalil Pasic ccw_dstream_rewind(&sch->cds); 385c9aacaadSPeter Maydell features.features = cpu_to_le32(features.features); 386f57ba058SHalil Pasic ccw_dstream_write(&sch->cds, features.features); 387a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(features); 388a5cf2bb4SCornelia Huck ret = 0; 389a5cf2bb4SCornelia Huck } 390a5cf2bb4SCornelia Huck break; 391a5cf2bb4SCornelia Huck case CCW_CMD_WRITE_FEAT: 392a5cf2bb4SCornelia Huck if (check_len) { 393a5cf2bb4SCornelia Huck if (ccw.count != sizeof(features)) { 394a5cf2bb4SCornelia Huck ret = -EINVAL; 395a5cf2bb4SCornelia Huck break; 396a5cf2bb4SCornelia Huck } 397a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(features)) { 398a5cf2bb4SCornelia Huck /* Can't execute command. */ 399a5cf2bb4SCornelia Huck ret = -EINVAL; 400a5cf2bb4SCornelia Huck break; 401a5cf2bb4SCornelia Huck } 402a5cf2bb4SCornelia Huck if (!ccw.cda) { 403a5cf2bb4SCornelia Huck ret = -EFAULT; 404a5cf2bb4SCornelia Huck } else { 405f57ba058SHalil Pasic ccw_dstream_read(&sch->cds, features); 406c9aacaadSPeter Maydell features.features = le32_to_cpu(features.features); 4076b8f1020SCornelia Huck if (features.index == 0) { 408c42767f2SThomas Huth virtio_set_features(vdev, 409c42767f2SThomas Huth (vdev->guest_features & 0xffffffff00000000ULL) | 410c42767f2SThomas Huth features.features); 411b4f8f9dfSCornelia Huck } else if ((features.index == 1) && (dev->revision >= 1)) { 412c42767f2SThomas Huth /* 413b4f8f9dfSCornelia Huck * If the guest did not negotiate at least revision 1, 414b4f8f9dfSCornelia Huck * we did not offer it any feature bits beyond 31. Such a 415b4f8f9dfSCornelia Huck * guest passing us any bit here is therefore buggy. 416c42767f2SThomas Huth */ 417c42767f2SThomas Huth virtio_set_features(vdev, 418c42767f2SThomas Huth (vdev->guest_features & 0x00000000ffffffffULL) | 419c42767f2SThomas Huth ((uint64_t)features.features << 32)); 420a5cf2bb4SCornelia Huck } else { 421a5cf2bb4SCornelia Huck /* 422a5cf2bb4SCornelia Huck * If the guest supports more feature bits, assert that it 423a5cf2bb4SCornelia Huck * passes us zeroes for those we don't support. 424a5cf2bb4SCornelia Huck */ 425a5cf2bb4SCornelia Huck if (features.features) { 42674a69e03SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 42774a69e03SAlistair Francis "Guest bug: features[%i]=%x (expected 0)", 428a5cf2bb4SCornelia Huck features.index, features.features); 429a5cf2bb4SCornelia Huck /* XXX: do a unit check here? */ 430a5cf2bb4SCornelia Huck } 431a5cf2bb4SCornelia Huck } 432a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(features); 433a5cf2bb4SCornelia Huck ret = 0; 434a5cf2bb4SCornelia Huck } 435a5cf2bb4SCornelia Huck break; 436a5cf2bb4SCornelia Huck case CCW_CMD_READ_CONF: 437a5cf2bb4SCornelia Huck if (check_len) { 438f24a6840SPaolo Bonzini if (ccw.count > vdev->config_len) { 439a5cf2bb4SCornelia Huck ret = -EINVAL; 440a5cf2bb4SCornelia Huck break; 441a5cf2bb4SCornelia Huck } 442a5cf2bb4SCornelia Huck } 443f24a6840SPaolo Bonzini len = MIN(ccw.count, vdev->config_len); 444a5cf2bb4SCornelia Huck if (!ccw.cda) { 445a5cf2bb4SCornelia Huck ret = -EFAULT; 446a5cf2bb4SCornelia Huck } else { 447f24a6840SPaolo Bonzini virtio_bus_get_vdev_config(&dev->bus, vdev->config); 448f57ba058SHalil Pasic ccw_dstream_write_buf(&sch->cds, vdev->config, len); 449a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - len; 450a5cf2bb4SCornelia Huck ret = 0; 451a5cf2bb4SCornelia Huck } 452a5cf2bb4SCornelia Huck break; 453a5cf2bb4SCornelia Huck case CCW_CMD_WRITE_CONF: 454a5cf2bb4SCornelia Huck if (check_len) { 455f24a6840SPaolo Bonzini if (ccw.count > vdev->config_len) { 456a5cf2bb4SCornelia Huck ret = -EINVAL; 457a5cf2bb4SCornelia Huck break; 458a5cf2bb4SCornelia Huck } 459a5cf2bb4SCornelia Huck } 460f24a6840SPaolo Bonzini len = MIN(ccw.count, vdev->config_len); 461a5cf2bb4SCornelia Huck if (!ccw.cda) { 462a5cf2bb4SCornelia Huck ret = -EFAULT; 463a5cf2bb4SCornelia Huck } else { 464f57ba058SHalil Pasic ret = ccw_dstream_read_buf(&sch->cds, vdev->config, len); 465f57ba058SHalil Pasic if (!ret) { 466f24a6840SPaolo Bonzini virtio_bus_set_vdev_config(&dev->bus, vdev->config); 467a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - len; 468a5cf2bb4SCornelia Huck } 469a5cf2bb4SCornelia Huck } 470a5cf2bb4SCornelia Huck break; 471e32652f7SPierre Morel case CCW_CMD_READ_STATUS: 472e32652f7SPierre Morel if (check_len) { 473e32652f7SPierre Morel if (ccw.count != sizeof(status)) { 474e32652f7SPierre Morel ret = -EINVAL; 475e32652f7SPierre Morel break; 476e32652f7SPierre Morel } 477e32652f7SPierre Morel } else if (ccw.count < sizeof(status)) { 478e32652f7SPierre Morel /* Can't execute command. */ 479e32652f7SPierre Morel ret = -EINVAL; 480e32652f7SPierre Morel break; 481e32652f7SPierre Morel } 482e32652f7SPierre Morel if (!ccw.cda) { 483e32652f7SPierre Morel ret = -EFAULT; 484e32652f7SPierre Morel } else { 485e32652f7SPierre Morel address_space_stb(&address_space_memory, ccw.cda, vdev->status, 486e32652f7SPierre Morel MEMTXATTRS_UNSPECIFIED, NULL); 4873c254ab8SLadi Prosek sch->curr_status.scsw.count = ccw.count - sizeof(vdev->status); 488e32652f7SPierre Morel ret = 0; 489e32652f7SPierre Morel } 490e32652f7SPierre Morel break; 491a5cf2bb4SCornelia Huck case CCW_CMD_WRITE_STATUS: 492a5cf2bb4SCornelia Huck if (check_len) { 493a5cf2bb4SCornelia Huck if (ccw.count != sizeof(status)) { 494a5cf2bb4SCornelia Huck ret = -EINVAL; 495a5cf2bb4SCornelia Huck break; 496a5cf2bb4SCornelia Huck } 497a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(status)) { 498a5cf2bb4SCornelia Huck /* Can't execute command. */ 499a5cf2bb4SCornelia Huck ret = -EINVAL; 500a5cf2bb4SCornelia Huck break; 501a5cf2bb4SCornelia Huck } 502a5cf2bb4SCornelia Huck if (!ccw.cda) { 503a5cf2bb4SCornelia Huck ret = -EFAULT; 504a5cf2bb4SCornelia Huck } else { 505f57ba058SHalil Pasic ccw_dstream_read(&sch->cds, status); 506b4436a0bSCornelia Huck if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { 507b4436a0bSCornelia Huck virtio_ccw_stop_ioeventfd(dev); 508b4436a0bSCornelia Huck } 5090b352fd6SCornelia Huck if (virtio_set_status(vdev, status) == 0) { 510f24a6840SPaolo Bonzini if (vdev->status == 0) { 511fa8b0ca5SCornelia Huck virtio_ccw_reset_virtio(dev, vdev); 512a5cf2bb4SCornelia Huck } 513b4436a0bSCornelia Huck if (status & VIRTIO_CONFIG_S_DRIVER_OK) { 514b4436a0bSCornelia Huck virtio_ccw_start_ioeventfd(dev); 515b4436a0bSCornelia Huck } 516a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(status); 517a5cf2bb4SCornelia Huck ret = 0; 5180b352fd6SCornelia Huck } else { 5190b352fd6SCornelia Huck /* Trigger a command reject. */ 5200b352fd6SCornelia Huck ret = -ENOSYS; 5210b352fd6SCornelia Huck } 522a5cf2bb4SCornelia Huck } 523a5cf2bb4SCornelia Huck break; 524a5cf2bb4SCornelia Huck case CCW_CMD_SET_IND: 525a5cf2bb4SCornelia Huck if (check_len) { 526a5cf2bb4SCornelia Huck if (ccw.count != sizeof(indicators)) { 527a5cf2bb4SCornelia Huck ret = -EINVAL; 528a5cf2bb4SCornelia Huck break; 529a5cf2bb4SCornelia Huck } 530a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(indicators)) { 531a5cf2bb4SCornelia Huck /* Can't execute command. */ 532a5cf2bb4SCornelia Huck ret = -EINVAL; 533a5cf2bb4SCornelia Huck break; 534a5cf2bb4SCornelia Huck } 5357e749462SCornelia Huck if (sch->thinint_active) { 5367e749462SCornelia Huck /* Trigger a command reject. */ 5377e749462SCornelia Huck ret = -ENOSYS; 5387e749462SCornelia Huck break; 5397e749462SCornelia Huck } 540797b6086SHalil Pasic if (virtio_get_num_queues(vdev) > NR_CLASSIC_INDICATOR_BITS) { 541797b6086SHalil Pasic /* More queues than indicator bits --> trigger a reject */ 542797b6086SHalil Pasic ret = -ENOSYS; 543797b6086SHalil Pasic break; 544797b6086SHalil Pasic } 545d1db1fa8SCornelia Huck if (!ccw.cda) { 546a5cf2bb4SCornelia Huck ret = -EFAULT; 547a5cf2bb4SCornelia Huck } else { 548f57ba058SHalil Pasic ccw_dstream_read(&sch->cds, indicators); 549c9aacaadSPeter Maydell indicators = be64_to_cpu(indicators); 5507bca3892SCornelia Huck dev->indicators = get_indicator(indicators, sizeof(uint64_t)); 551a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(indicators); 552a5cf2bb4SCornelia Huck ret = 0; 553a5cf2bb4SCornelia Huck } 554a5cf2bb4SCornelia Huck break; 555a5cf2bb4SCornelia Huck case CCW_CMD_SET_CONF_IND: 556a5cf2bb4SCornelia Huck if (check_len) { 557a5cf2bb4SCornelia Huck if (ccw.count != sizeof(indicators)) { 558a5cf2bb4SCornelia Huck ret = -EINVAL; 559a5cf2bb4SCornelia Huck break; 560a5cf2bb4SCornelia Huck } 561a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(indicators)) { 562a5cf2bb4SCornelia Huck /* Can't execute command. */ 563a5cf2bb4SCornelia Huck ret = -EINVAL; 564a5cf2bb4SCornelia Huck break; 565a5cf2bb4SCornelia Huck } 566d1db1fa8SCornelia Huck if (!ccw.cda) { 567a5cf2bb4SCornelia Huck ret = -EFAULT; 568a5cf2bb4SCornelia Huck } else { 569f57ba058SHalil Pasic ccw_dstream_read(&sch->cds, indicators); 570c9aacaadSPeter Maydell indicators = be64_to_cpu(indicators); 5717bca3892SCornelia Huck dev->indicators2 = get_indicator(indicators, sizeof(uint64_t)); 572a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(indicators); 573a5cf2bb4SCornelia Huck ret = 0; 574a5cf2bb4SCornelia Huck } 575a5cf2bb4SCornelia Huck break; 576a5cf2bb4SCornelia Huck case CCW_CMD_READ_VQ_CONF: 577a5cf2bb4SCornelia Huck if (check_len) { 578a5cf2bb4SCornelia Huck if (ccw.count != sizeof(vq_config)) { 579a5cf2bb4SCornelia Huck ret = -EINVAL; 580a5cf2bb4SCornelia Huck break; 581a5cf2bb4SCornelia Huck } 582a5cf2bb4SCornelia Huck } else if (ccw.count < sizeof(vq_config)) { 583a5cf2bb4SCornelia Huck /* Can't execute command. */ 584a5cf2bb4SCornelia Huck ret = -EINVAL; 585a5cf2bb4SCornelia Huck break; 586a5cf2bb4SCornelia Huck } 587a5cf2bb4SCornelia Huck if (!ccw.cda) { 588a5cf2bb4SCornelia Huck ret = -EFAULT; 589a5cf2bb4SCornelia Huck } else { 590f57ba058SHalil Pasic ccw_dstream_read(&sch->cds, vq_config.index); 591c9aacaadSPeter Maydell vq_config.index = be16_to_cpu(vq_config.index); 592b1914b82SHalil Pasic if (vq_config.index >= VIRTIO_QUEUE_MAX) { 593d03a3630SCornelia Huck ret = -EINVAL; 594d03a3630SCornelia Huck break; 595d03a3630SCornelia Huck } 596f24a6840SPaolo Bonzini vq_config.num_max = virtio_queue_get_num(vdev, 597a5cf2bb4SCornelia Huck vq_config.index); 598c9aacaadSPeter Maydell vq_config.num_max = cpu_to_be16(vq_config.num_max); 599f57ba058SHalil Pasic ccw_dstream_write(&sch->cds, vq_config.num_max); 600a5cf2bb4SCornelia Huck sch->curr_status.scsw.count = ccw.count - sizeof(vq_config); 601a5cf2bb4SCornelia Huck ret = 0; 602a5cf2bb4SCornelia Huck } 603a5cf2bb4SCornelia Huck break; 6047e749462SCornelia Huck case CCW_CMD_SET_IND_ADAPTER: 6057e749462SCornelia Huck if (check_len) { 606f57ba058SHalil Pasic if (ccw.count != sizeof(thinint)) { 6077e749462SCornelia Huck ret = -EINVAL; 6087e749462SCornelia Huck break; 6097e749462SCornelia Huck } 610f57ba058SHalil Pasic } else if (ccw.count < sizeof(thinint)) { 6117e749462SCornelia Huck /* Can't execute command. */ 6127e749462SCornelia Huck ret = -EINVAL; 6137e749462SCornelia Huck break; 6147e749462SCornelia Huck } 6157e749462SCornelia Huck if (!ccw.cda) { 6167e749462SCornelia Huck ret = -EFAULT; 6177e749462SCornelia Huck } else if (dev->indicators && !sch->thinint_active) { 6187e749462SCornelia Huck /* Trigger a command reject. */ 6197e749462SCornelia Huck ret = -ENOSYS; 6207e749462SCornelia Huck } else { 621f57ba058SHalil Pasic if (ccw_dstream_read(&sch->cds, thinint)) { 6227e749462SCornelia Huck ret = -EFAULT; 6237e749462SCornelia Huck } else { 624c9aacaadSPeter Maydell thinint.ind_bit = be64_to_cpu(thinint.ind_bit); 625c9aacaadSPeter Maydell thinint.summary_indicator = 626c9aacaadSPeter Maydell be64_to_cpu(thinint.summary_indicator); 627c9aacaadSPeter Maydell thinint.device_indicator = 628c9aacaadSPeter Maydell be64_to_cpu(thinint.device_indicator); 6297d45285fSCornelia Huck 6307bca3892SCornelia Huck dev->summary_indicator = 631f57ba058SHalil Pasic get_indicator(thinint.summary_indicator, sizeof(uint8_t)); 6327d45285fSCornelia Huck dev->indicators = 633f57ba058SHalil Pasic get_indicator(thinint.device_indicator, 634f57ba058SHalil Pasic thinint.ind_bit / 8 + 1); 635f57ba058SHalil Pasic dev->thinint_isc = thinint.isc; 636f57ba058SHalil Pasic dev->routes.adapter.ind_offset = thinint.ind_bit; 637d426d9fbSCornelia Huck dev->routes.adapter.summary_offset = 7; 638dde522bbSFei Li dev->routes.adapter.adapter_id = css_get_adapter_id( 639dde522bbSFei Li CSS_IO_ADAPTER_VIRTIO, 640dde522bbSFei Li dev->thinint_isc); 6417bca3892SCornelia Huck sch->thinint_active = ((dev->indicators != NULL) && 6427bca3892SCornelia Huck (dev->summary_indicator != NULL)); 643f57ba058SHalil Pasic sch->curr_status.scsw.count = ccw.count - sizeof(thinint); 6447e749462SCornelia Huck ret = 0; 6457e749462SCornelia Huck } 6467e749462SCornelia Huck } 6477e749462SCornelia Huck break; 648c42767f2SThomas Huth case CCW_CMD_SET_VIRTIO_REV: 649c42767f2SThomas Huth len = sizeof(revinfo); 650c42767f2SThomas Huth if (ccw.count < len) { 651c42767f2SThomas Huth ret = -EINVAL; 652c42767f2SThomas Huth break; 653c42767f2SThomas Huth } 654c42767f2SThomas Huth if (!ccw.cda) { 655c42767f2SThomas Huth ret = -EFAULT; 656c42767f2SThomas Huth break; 657c42767f2SThomas Huth } 658f57ba058SHalil Pasic ccw_dstream_read_buf(&sch->cds, &revinfo, 4); 659c9aacaadSPeter Maydell revinfo.revision = be16_to_cpu(revinfo.revision); 660c9aacaadSPeter Maydell revinfo.length = be16_to_cpu(revinfo.length); 661c42767f2SThomas Huth if (ccw.count < len + revinfo.length || 662c42767f2SThomas Huth (check_len && ccw.count > len + revinfo.length)) { 663c42767f2SThomas Huth ret = -EINVAL; 664c42767f2SThomas Huth break; 665c42767f2SThomas Huth } 666c42767f2SThomas Huth /* 667c42767f2SThomas Huth * Once we start to support revisions with additional data, we'll 668c42767f2SThomas Huth * need to fetch it here. Nothing to do for now, though. 669c42767f2SThomas Huth */ 670c42767f2SThomas Huth if (dev->revision >= 0 || 67147e13dfdSHalil Pasic revinfo.revision > virtio_ccw_rev_max(dev) || 67247e13dfdSHalil Pasic (dev->force_revision_1 && !revinfo.revision)) { 673c42767f2SThomas Huth ret = -ENOSYS; 674c42767f2SThomas Huth break; 675c42767f2SThomas Huth } 676c42767f2SThomas Huth ret = 0; 677c42767f2SThomas Huth dev->revision = revinfo.revision; 678c42767f2SThomas Huth break; 679a5cf2bb4SCornelia Huck default: 6808d034a6fSCornelia Huck ret = -ENOSYS; 681a5cf2bb4SCornelia Huck break; 682a5cf2bb4SCornelia Huck } 683a5cf2bb4SCornelia Huck return ret; 684a5cf2bb4SCornelia Huck } 685a5cf2bb4SCornelia Huck 686c42767f2SThomas Huth static void virtio_sch_disable_cb(SubchDev *sch) 687c42767f2SThomas Huth { 688c42767f2SThomas Huth VirtioCcwDevice *dev = sch->driver_data; 689c42767f2SThomas Huth 690c42767f2SThomas Huth dev->revision = -1; 691c42767f2SThomas Huth } 692c42767f2SThomas Huth 6931fa75523SCornelia Huck static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) 694a5cf2bb4SCornelia Huck { 6951fa75523SCornelia Huck VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev); 696b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev); 697d8d98db5SDong Jia Shi CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); 698817d4a6bSDong Jia Shi SubchDev *sch; 699cf249935SSascha Silbe Error *err = NULL; 7003c5fd807SCornelia Huck int i; 701a5cf2bb4SCornelia Huck 70236699ab4SCornelia Huck sch = css_create_sch(ccw_dev->devno, errp); 703cf249935SSascha Silbe if (!sch) { 704cf249935SSascha Silbe return; 705cf249935SSascha Silbe } 70647e13dfdSHalil Pasic if (!virtio_ccw_rev_max(dev) && dev->force_revision_1) { 70747e13dfdSHalil Pasic error_setg(&err, "Invalid value of property max_rev " 70847e13dfdSHalil Pasic "(is %d expected >= 1)", virtio_ccw_rev_max(dev)); 709d8d98db5SDong Jia Shi goto out_err; 71047e13dfdSHalil Pasic } 711a5cf2bb4SCornelia Huck 712a5cf2bb4SCornelia Huck sch->driver_data = dev; 713a5cf2bb4SCornelia Huck sch->ccw_cb = virtio_ccw_cb; 714c42767f2SThomas Huth sch->disable_cb = virtio_sch_disable_cb; 715a5cf2bb4SCornelia Huck sch->id.reserved = 0xff; 716a5cf2bb4SCornelia Huck sch->id.cu_type = VIRTIO_CCW_CU_TYPE; 717bab482d7SXiao Feng Ren sch->do_subchannel_work = do_subchannel_work_virtual; 718b804e8a6SJing Liu ccw_dev->sch = sch; 719cf249935SSascha Silbe dev->indicators = NULL; 720c42767f2SThomas Huth dev->revision = -1; 7213c5fd807SCornelia Huck for (i = 0; i < ADAPTER_ROUTES_MAX_GSI; i++) { 7223c5fd807SCornelia Huck dev->routes.gsi[i] = -1; 7233c5fd807SCornelia Huck } 724cf249935SSascha Silbe css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); 725cf249935SSascha Silbe 726cf249935SSascha Silbe trace_virtio_ccw_new_device( 727cf249935SSascha Silbe sch->cssid, sch->ssid, sch->schid, sch->devno, 7282a78ac66SDong Jia Shi ccw_dev->devno.valid ? "user-configured" : "auto-configured"); 729c42767f2SThomas Huth 730cda3c19fSQingFeng Hao if (kvm_enabled() && !kvm_eventfds_enabled()) { 731ca2b413cSPaolo Bonzini dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; 732ca2b413cSPaolo Bonzini } 733ca2b413cSPaolo Bonzini 7341fa75523SCornelia Huck if (k->realize) { 7351fa75523SCornelia Huck k->realize(dev, &err); 7361fa75523SCornelia Huck if (err) { 737d8d98db5SDong Jia Shi goto out_err; 738d8d98db5SDong Jia Shi } 739d8d98db5SDong Jia Shi } 740d8d98db5SDong Jia Shi 741d8d98db5SDong Jia Shi ck->realize(ccw_dev, &err); 742d8d98db5SDong Jia Shi if (err) { 743d8d98db5SDong Jia Shi goto out_err; 744d8d98db5SDong Jia Shi } 745d8d98db5SDong Jia Shi 746d8d98db5SDong Jia Shi return; 747d8d98db5SDong Jia Shi 748d8d98db5SDong Jia Shi out_err: 7491fa75523SCornelia Huck error_propagate(errp, err); 75006e686eaSCornelia Huck css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); 751b804e8a6SJing Liu ccw_dev->sch = NULL; 752a5cf2bb4SCornelia Huck g_free(sch); 753a5cf2bb4SCornelia Huck } 754a5cf2bb4SCornelia Huck 755b69c3c21SMarkus Armbruster static void virtio_ccw_device_unrealize(VirtioCcwDevice *dev) 756a5cf2bb4SCornelia Huck { 7572d6ff33aSThomas Huth VirtIOCCWDeviceClass *dc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); 758b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev); 759b804e8a6SJing Liu SubchDev *sch = ccw_dev->sch; 760a5cf2bb4SCornelia Huck 7612d6ff33aSThomas Huth if (dc->unrealize) { 762b69c3c21SMarkus Armbruster dc->unrealize(dev); 7632d6ff33aSThomas Huth } 7642d6ff33aSThomas Huth 765a5cf2bb4SCornelia Huck if (sch) { 766a5cf2bb4SCornelia Huck css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); 767a5cf2bb4SCornelia Huck g_free(sch); 76824118af8SNia Alarie ccw_dev->sch = NULL; 769a5cf2bb4SCornelia Huck } 7707bca3892SCornelia Huck if (dev->indicators) { 771d426d9fbSCornelia Huck release_indicator(&dev->routes.adapter, dev->indicators); 7727bca3892SCornelia Huck dev->indicators = NULL; 7737bca3892SCornelia Huck } 774a5cf2bb4SCornelia Huck } 775a5cf2bb4SCornelia Huck 776a5cf2bb4SCornelia Huck /* DeviceState to VirtioCcwDevice. Note: used on datapath, 777a5cf2bb4SCornelia Huck * be careful and test performance if you change this. 778a5cf2bb4SCornelia Huck */ 779a5cf2bb4SCornelia Huck static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) 780a5cf2bb4SCornelia Huck { 781b804e8a6SJing Liu CcwDevice *ccw_dev = to_ccw_dev_fast(d); 782b804e8a6SJing Liu 783b804e8a6SJing Liu return container_of(ccw_dev, VirtioCcwDevice, parent_obj); 784a5cf2bb4SCornelia Huck } 785a5cf2bb4SCornelia Huck 7867e749462SCornelia Huck static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, 7877e749462SCornelia Huck uint8_t to_be_set) 7887e749462SCornelia Huck { 789*1a8242f7SHalil Pasic uint8_t expected, actual; 7907e749462SCornelia Huck hwaddr len = 1; 791*1a8242f7SHalil Pasic /* avoid multiple fetches */ 792*1a8242f7SHalil Pasic uint8_t volatile *ind_addr; 7937e749462SCornelia Huck 79485eb7c18SPhilippe Mathieu-Daudé ind_addr = cpu_physical_memory_map(ind_loc, &len, true); 7957e749462SCornelia Huck if (!ind_addr) { 7967e749462SCornelia Huck error_report("%s(%x.%x.%04x): unable to access indicator", 7977e749462SCornelia Huck __func__, sch->cssid, sch->ssid, sch->schid); 7987e749462SCornelia Huck return -1; 7997e749462SCornelia Huck } 800*1a8242f7SHalil Pasic actual = *ind_addr; 8017e749462SCornelia Huck do { 802*1a8242f7SHalil Pasic expected = actual; 803*1a8242f7SHalil Pasic actual = atomic_cmpxchg(ind_addr, expected, expected | to_be_set); 804*1a8242f7SHalil Pasic } while (actual != expected); 805*1a8242f7SHalil Pasic trace_virtio_ccw_set_ind(ind_loc, actual, actual | to_be_set); 806*1a8242f7SHalil Pasic cpu_physical_memory_unmap((void *)ind_addr, len, 1, len); 8077e749462SCornelia Huck 808*1a8242f7SHalil Pasic return actual; 8097e749462SCornelia Huck } 8107e749462SCornelia Huck 811a5cf2bb4SCornelia Huck static void virtio_ccw_notify(DeviceState *d, uint16_t vector) 812a5cf2bb4SCornelia Huck { 813a5cf2bb4SCornelia Huck VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); 814b804e8a6SJing Liu CcwDevice *ccw_dev = to_ccw_dev_fast(d); 815b804e8a6SJing Liu SubchDev *sch = ccw_dev->sch; 816a5cf2bb4SCornelia Huck uint64_t indicators; 817a5cf2bb4SCornelia Huck 8187a5342e7SHalil Pasic if (vector == VIRTIO_NO_VECTOR) { 819a5cf2bb4SCornelia Huck return; 820a5cf2bb4SCornelia Huck } 8217a5342e7SHalil Pasic /* 8227a5342e7SHalil Pasic * vector < VIRTIO_QUEUE_MAX: notification for a virtqueue 8237a5342e7SHalil Pasic * vector == VIRTIO_QUEUE_MAX: configuration change notification 8247a5342e7SHalil Pasic * bits beyond that are unused and should never be notified for 8257a5342e7SHalil Pasic */ 8267a5342e7SHalil Pasic assert(vector <= VIRTIO_QUEUE_MAX); 827a5cf2bb4SCornelia Huck 828b1914b82SHalil Pasic if (vector < VIRTIO_QUEUE_MAX) { 8297c486976SCornelia Huck if (!dev->indicators) { 8307c486976SCornelia Huck return; 8317c486976SCornelia Huck } 8327e749462SCornelia Huck if (sch->thinint_active) { 8337e749462SCornelia Huck /* 8347e749462SCornelia Huck * In the adapter interrupt case, indicators points to a 8357e749462SCornelia Huck * memory area that may be (way) larger than 64 bit and 8367e749462SCornelia Huck * ind_bit indicates the start of the indicators in a big 8377e749462SCornelia Huck * endian notation. 8387e749462SCornelia Huck */ 839d426d9fbSCornelia Huck uint64_t ind_bit = dev->routes.adapter.ind_offset; 840d426d9fbSCornelia Huck 8417bca3892SCornelia Huck virtio_set_ind_atomic(sch, dev->indicators->addr + 842d426d9fbSCornelia Huck (ind_bit + vector) / 8, 843d426d9fbSCornelia Huck 0x80 >> ((ind_bit + vector) % 8)); 8447bca3892SCornelia Huck if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr, 8457e749462SCornelia Huck 0x01)) { 84625a08b8dSYi Min Zhao css_adapter_interrupt(CSS_IO_ADAPTER_VIRTIO, dev->thinint_isc); 8477e749462SCornelia Huck } 8487e749462SCornelia Huck } else { 8497a5342e7SHalil Pasic assert(vector < NR_CLASSIC_INDICATOR_BITS); 85042874d3aSPeter Maydell indicators = address_space_ldq(&address_space_memory, 85142874d3aSPeter Maydell dev->indicators->addr, 85242874d3aSPeter Maydell MEMTXATTRS_UNSPECIFIED, 85342874d3aSPeter Maydell NULL); 85419380b1bSCornelia Huck indicators |= 1ULL << vector; 85542874d3aSPeter Maydell address_space_stq(&address_space_memory, dev->indicators->addr, 85642874d3aSPeter Maydell indicators, MEMTXATTRS_UNSPECIFIED, NULL); 8577e749462SCornelia Huck css_conditional_io_interrupt(sch); 8587e749462SCornelia Huck } 859a5cf2bb4SCornelia Huck } else { 8607c486976SCornelia Huck if (!dev->indicators2) { 8617c486976SCornelia Huck return; 8627c486976SCornelia Huck } 86342874d3aSPeter Maydell indicators = address_space_ldq(&address_space_memory, 86442874d3aSPeter Maydell dev->indicators2->addr, 86542874d3aSPeter Maydell MEMTXATTRS_UNSPECIFIED, 86642874d3aSPeter Maydell NULL); 8677a5342e7SHalil Pasic indicators |= 1ULL; 86842874d3aSPeter Maydell address_space_stq(&address_space_memory, dev->indicators2->addr, 86942874d3aSPeter Maydell indicators, MEMTXATTRS_UNSPECIFIED, NULL); 870a5cf2bb4SCornelia Huck css_conditional_io_interrupt(sch); 8717e749462SCornelia Huck } 872a5cf2bb4SCornelia Huck } 873a5cf2bb4SCornelia Huck 874a5cf2bb4SCornelia Huck static void virtio_ccw_reset(DeviceState *d) 875a5cf2bb4SCornelia Huck { 876a5cf2bb4SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 877f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 878838fb84fSCornelia Huck VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); 879a5cf2bb4SCornelia Huck 880fa8b0ca5SCornelia Huck virtio_ccw_reset_virtio(dev, vdev); 881838fb84fSCornelia Huck if (vdc->parent_reset) { 882838fb84fSCornelia Huck vdc->parent_reset(d); 883838fb84fSCornelia Huck } 884a5cf2bb4SCornelia Huck } 885a5cf2bb4SCornelia Huck 886b4436a0bSCornelia Huck static void virtio_ccw_vmstate_change(DeviceState *d, bool running) 887b4436a0bSCornelia Huck { 888b4436a0bSCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 889b4436a0bSCornelia Huck 890b4436a0bSCornelia Huck if (running) { 891b4436a0bSCornelia Huck virtio_ccw_start_ioeventfd(dev); 892b4436a0bSCornelia Huck } else { 893b4436a0bSCornelia Huck virtio_ccw_stop_ioeventfd(dev); 894b4436a0bSCornelia Huck } 895b4436a0bSCornelia Huck } 896b4436a0bSCornelia Huck 897320ce850SCornelia Huck static bool virtio_ccw_query_guest_notifiers(DeviceState *d) 898320ce850SCornelia Huck { 899b804e8a6SJing Liu CcwDevice *dev = CCW_DEVICE(d); 900320ce850SCornelia Huck 901320ce850SCornelia Huck return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA); 902320ce850SCornelia Huck } 903320ce850SCornelia Huck 904d426d9fbSCornelia Huck static int virtio_ccw_get_mappings(VirtioCcwDevice *dev) 905d426d9fbSCornelia Huck { 906d426d9fbSCornelia Huck int r; 907b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev); 908d426d9fbSCornelia Huck 909b804e8a6SJing Liu if (!ccw_dev->sch->thinint_active) { 910d426d9fbSCornelia Huck return -EINVAL; 911d426d9fbSCornelia Huck } 912d426d9fbSCornelia Huck 913d426d9fbSCornelia Huck r = map_indicator(&dev->routes.adapter, dev->summary_indicator); 914d426d9fbSCornelia Huck if (r) { 915d426d9fbSCornelia Huck return r; 916d426d9fbSCornelia Huck } 917d426d9fbSCornelia Huck r = map_indicator(&dev->routes.adapter, dev->indicators); 918d426d9fbSCornelia Huck if (r) { 919d426d9fbSCornelia Huck return r; 920d426d9fbSCornelia Huck } 921d426d9fbSCornelia Huck dev->routes.adapter.summary_addr = dev->summary_indicator->map; 922d426d9fbSCornelia Huck dev->routes.adapter.ind_addr = dev->indicators->map; 923d426d9fbSCornelia Huck 924d426d9fbSCornelia Huck return 0; 925d426d9fbSCornelia Huck } 926d426d9fbSCornelia Huck 927d426d9fbSCornelia Huck static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs) 928d426d9fbSCornelia Huck { 929d426d9fbSCornelia Huck int i; 930d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 931d426d9fbSCornelia Huck int ret; 932d426d9fbSCornelia Huck S390FLICState *fs = s390_get_flic(); 9336762808fSDavid Hildenbrand S390FLICStateClass *fsc = s390_get_flic_class(fs); 934d426d9fbSCornelia Huck 935d426d9fbSCornelia Huck ret = virtio_ccw_get_mappings(dev); 936d426d9fbSCornelia Huck if (ret) { 937d426d9fbSCornelia Huck return ret; 938d426d9fbSCornelia Huck } 939d426d9fbSCornelia Huck for (i = 0; i < nvqs; i++) { 940d426d9fbSCornelia Huck if (!virtio_queue_get_num(vdev, i)) { 941d426d9fbSCornelia Huck break; 942d426d9fbSCornelia Huck } 943d426d9fbSCornelia Huck } 944d426d9fbSCornelia Huck dev->routes.num_routes = i; 945d426d9fbSCornelia Huck return fsc->add_adapter_routes(fs, &dev->routes); 946d426d9fbSCornelia Huck } 947d426d9fbSCornelia Huck 948d426d9fbSCornelia Huck static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs) 949d426d9fbSCornelia Huck { 950d426d9fbSCornelia Huck S390FLICState *fs = s390_get_flic(); 9516762808fSDavid Hildenbrand S390FLICStateClass *fsc = s390_get_flic_class(fs); 952d426d9fbSCornelia Huck 953d426d9fbSCornelia Huck fsc->release_adapter_routes(fs, &dev->routes); 954d426d9fbSCornelia Huck } 955d426d9fbSCornelia Huck 956d426d9fbSCornelia Huck static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n) 957d426d9fbSCornelia Huck { 958d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 959d426d9fbSCornelia Huck VirtQueue *vq = virtio_get_queue(vdev, n); 960d426d9fbSCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); 961d426d9fbSCornelia Huck 9621c9b71a7SEric Auger return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, notifier, NULL, 963d426d9fbSCornelia Huck dev->routes.gsi[n]); 964d426d9fbSCornelia Huck } 965d426d9fbSCornelia Huck 966d426d9fbSCornelia Huck static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n) 967d426d9fbSCornelia Huck { 968d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 969d426d9fbSCornelia Huck VirtQueue *vq = virtio_get_queue(vdev, n); 970d426d9fbSCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); 971d426d9fbSCornelia Huck int ret; 972d426d9fbSCornelia Huck 9731c9b71a7SEric Auger ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, notifier, 974d426d9fbSCornelia Huck dev->routes.gsi[n]); 975d426d9fbSCornelia Huck assert(ret == 0); 976d426d9fbSCornelia Huck } 977d426d9fbSCornelia Huck 978320ce850SCornelia Huck static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, 979320ce850SCornelia Huck bool assign, bool with_irqfd) 980320ce850SCornelia Huck { 981f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 982f24a6840SPaolo Bonzini VirtQueue *vq = virtio_get_queue(vdev, n); 983320ce850SCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); 984f24a6840SPaolo Bonzini VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); 985320ce850SCornelia Huck 986320ce850SCornelia Huck if (assign) { 987320ce850SCornelia Huck int r = event_notifier_init(notifier, 0); 988320ce850SCornelia Huck 989320ce850SCornelia Huck if (r < 0) { 990320ce850SCornelia Huck return r; 991320ce850SCornelia Huck } 992320ce850SCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); 993d426d9fbSCornelia Huck if (with_irqfd) { 994d426d9fbSCornelia Huck r = virtio_ccw_add_irqfd(dev, n); 995d426d9fbSCornelia Huck if (r) { 996d426d9fbSCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, false, 997d426d9fbSCornelia Huck with_irqfd); 998d426d9fbSCornelia Huck return r; 999d426d9fbSCornelia Huck } 1000d426d9fbSCornelia Huck } 1001d426d9fbSCornelia Huck /* 1002d426d9fbSCornelia Huck * We do not support individual masking for channel devices, so we 1003d426d9fbSCornelia Huck * need to manually trigger any guest masking callbacks here. 1004320ce850SCornelia Huck */ 10052858bc68SWei Huang if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) { 1006f24a6840SPaolo Bonzini k->guest_notifier_mask(vdev, n, false); 1007320ce850SCornelia Huck } 1008320ce850SCornelia Huck /* get lost events and re-inject */ 1009320ce850SCornelia Huck if (k->guest_notifier_pending && 1010f24a6840SPaolo Bonzini k->guest_notifier_pending(vdev, n)) { 1011320ce850SCornelia Huck event_notifier_set(notifier); 1012320ce850SCornelia Huck } 1013320ce850SCornelia Huck } else { 10142858bc68SWei Huang if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) { 1015f24a6840SPaolo Bonzini k->guest_notifier_mask(vdev, n, true); 1016320ce850SCornelia Huck } 1017d426d9fbSCornelia Huck if (with_irqfd) { 1018d426d9fbSCornelia Huck virtio_ccw_remove_irqfd(dev, n); 1019d426d9fbSCornelia Huck } 1020320ce850SCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); 1021320ce850SCornelia Huck event_notifier_cleanup(notifier); 1022320ce850SCornelia Huck } 1023320ce850SCornelia Huck return 0; 1024320ce850SCornelia Huck } 1025320ce850SCornelia Huck 1026320ce850SCornelia Huck static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs, 1027320ce850SCornelia Huck bool assigned) 1028320ce850SCornelia Huck { 1029320ce850SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1030f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1031b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(d); 1032b804e8a6SJing Liu bool with_irqfd = ccw_dev->sch->thinint_active && kvm_irqfds_enabled(); 1033320ce850SCornelia Huck int r, n; 1034320ce850SCornelia Huck 1035d426d9fbSCornelia Huck if (with_irqfd && assigned) { 1036d426d9fbSCornelia Huck /* irq routes need to be set up before assigning irqfds */ 1037d426d9fbSCornelia Huck r = virtio_ccw_setup_irqroutes(dev, nvqs); 1038d426d9fbSCornelia Huck if (r < 0) { 1039d426d9fbSCornelia Huck goto irqroute_error; 1040d426d9fbSCornelia Huck } 1041d426d9fbSCornelia Huck } 1042320ce850SCornelia Huck for (n = 0; n < nvqs; n++) { 1043320ce850SCornelia Huck if (!virtio_queue_get_num(vdev, n)) { 1044320ce850SCornelia Huck break; 1045320ce850SCornelia Huck } 1046d426d9fbSCornelia Huck r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd); 1047320ce850SCornelia Huck if (r < 0) { 1048320ce850SCornelia Huck goto assign_error; 1049320ce850SCornelia Huck } 1050320ce850SCornelia Huck } 1051d426d9fbSCornelia Huck if (with_irqfd && !assigned) { 1052d426d9fbSCornelia Huck /* release irq routes after irqfds have been released */ 1053d426d9fbSCornelia Huck virtio_ccw_release_irqroutes(dev, nvqs); 1054d426d9fbSCornelia Huck } 1055320ce850SCornelia Huck return 0; 1056320ce850SCornelia Huck 1057320ce850SCornelia Huck assign_error: 1058320ce850SCornelia Huck while (--n >= 0) { 1059320ce850SCornelia Huck virtio_ccw_set_guest_notifier(dev, n, !assigned, false); 1060320ce850SCornelia Huck } 1061d426d9fbSCornelia Huck irqroute_error: 1062d426d9fbSCornelia Huck if (with_irqfd && assigned) { 1063d426d9fbSCornelia Huck virtio_ccw_release_irqroutes(dev, nvqs); 1064d426d9fbSCornelia Huck } 1065320ce850SCornelia Huck return r; 1066320ce850SCornelia Huck } 1067320ce850SCornelia Huck 1068bcb2b582SJens Freimann static void virtio_ccw_save_queue(DeviceState *d, int n, QEMUFile *f) 1069bcb2b582SJens Freimann { 1070bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1071bcb2b582SJens Freimann VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1072bcb2b582SJens Freimann 1073bcb2b582SJens Freimann qemu_put_be16(f, virtio_queue_vector(vdev, n)); 1074bcb2b582SJens Freimann } 1075bcb2b582SJens Freimann 1076bcb2b582SJens Freimann static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f) 1077bcb2b582SJens Freimann { 1078bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1079bcb2b582SJens Freimann VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1080bcb2b582SJens Freimann uint16_t vector; 1081bcb2b582SJens Freimann 1082bcb2b582SJens Freimann qemu_get_be16s(f, &vector); 1083bcb2b582SJens Freimann virtio_queue_set_vector(vdev, n , vector); 1084bcb2b582SJens Freimann 1085bcb2b582SJens Freimann return 0; 1086bcb2b582SJens Freimann } 1087bcb2b582SJens Freimann 1088bcb2b582SJens Freimann static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) 1089bcb2b582SJens Freimann { 1090bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1091517ff12cSHalil Pasic vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL); 1092bcb2b582SJens Freimann } 1093bcb2b582SJens Freimann 1094bcb2b582SJens Freimann static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) 1095bcb2b582SJens Freimann { 1096bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1097517ff12cSHalil Pasic return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1); 1098bcb2b582SJens Freimann } 1099bcb2b582SJens Freimann 1100d1b4259fSMaxime Coquelin static void virtio_ccw_pre_plugged(DeviceState *d, Error **errp) 1101d1b4259fSMaxime Coquelin { 1102d1b4259fSMaxime Coquelin VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1103d1b4259fSMaxime Coquelin VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1104d1b4259fSMaxime Coquelin 1105d1b4259fSMaxime Coquelin if (dev->max_rev >= 1) { 1106d1b4259fSMaxime Coquelin virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1); 1107d1b4259fSMaxime Coquelin } 1108d1b4259fSMaxime Coquelin } 1109d1b4259fSMaxime Coquelin 1110fb846a09SCornelia Huck /* This is called by virtio-bus just after the device is plugged. */ 1111e8398045SJason Wang static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) 1112fb846a09SCornelia Huck { 1113fb846a09SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 111410ceaa1eSJason Wang VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1115b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(d); 1116b804e8a6SJing Liu SubchDev *sch = ccw_dev->sch; 111710ceaa1eSJason Wang int n = virtio_get_num_queues(vdev); 11180708afa7SHalil Pasic S390FLICState *flic = s390_get_flic(); 111910ceaa1eSJason Wang 1120d1b4259fSMaxime Coquelin if (!virtio_has_feature(vdev->host_features, VIRTIO_F_VERSION_1)) { 1121d1b4259fSMaxime Coquelin dev->max_rev = 0; 1122d1b4259fSMaxime Coquelin } 1123d1b4259fSMaxime Coquelin 1124b1914b82SHalil Pasic if (virtio_get_num_queues(vdev) > VIRTIO_QUEUE_MAX) { 1125b34aee54SMichael Tokarev error_setg(errp, "The number of virtqueues %d " 1126b1914b82SHalil Pasic "exceeds virtio limit %d", n, 1127b1914b82SHalil Pasic VIRTIO_QUEUE_MAX); 112810ceaa1eSJason Wang return; 112910ceaa1eSJason Wang } 11300708afa7SHalil Pasic if (virtio_get_num_queues(vdev) > flic->adapter_routes_max_batch) { 11310708afa7SHalil Pasic error_setg(errp, "The number of virtqueues %d " 11320708afa7SHalil Pasic "exceeds flic adapter route limit %d", n, 11330708afa7SHalil Pasic flic->adapter_routes_max_batch); 11340708afa7SHalil Pasic return; 11350708afa7SHalil Pasic } 1136fb846a09SCornelia Huck 1137fb846a09SCornelia Huck sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus); 1138fb846a09SCornelia Huck 1139542571d5SCornelia Huck 1140fb846a09SCornelia Huck css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1141fb846a09SCornelia Huck d->hotplugged, 1); 1142fb846a09SCornelia Huck } 1143fb846a09SCornelia Huck 1144fb846a09SCornelia Huck static void virtio_ccw_device_unplugged(DeviceState *d) 1145fb846a09SCornelia Huck { 1146fb846a09SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1147fb846a09SCornelia Huck 1148fb846a09SCornelia Huck virtio_ccw_stop_ioeventfd(dev); 1149fb846a09SCornelia Huck } 1150a5cf2bb4SCornelia Huck /**************** Virtio-ccw Bus Device Descriptions *******************/ 1151a5cf2bb4SCornelia Huck 11525e5ced38SMarkus Armbruster static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp) 1153a5cf2bb4SCornelia Huck { 1154a5cf2bb4SCornelia Huck VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; 1155a5cf2bb4SCornelia Huck 11561bf4d7aaSAndreas Färber virtio_ccw_bus_new(&_dev->bus, sizeof(_dev->bus), _dev); 11571fa75523SCornelia Huck virtio_ccw_device_realize(_dev, errp); 1158a5cf2bb4SCornelia Huck } 1159a5cf2bb4SCornelia Huck 1160b69c3c21SMarkus Armbruster static void virtio_ccw_busdev_unrealize(DeviceState *dev) 1161a5cf2bb4SCornelia Huck { 1162a5cf2bb4SCornelia Huck VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; 1163a5cf2bb4SCornelia Huck 1164b69c3c21SMarkus Armbruster virtio_ccw_device_unrealize(_dev); 1165a5cf2bb4SCornelia Huck } 1166a5cf2bb4SCornelia Huck 1167b804e8a6SJing Liu static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, 1168277bc95eSIgor Mammedov DeviceState *dev, Error **errp) 1169a5cf2bb4SCornelia Huck { 1170b804e8a6SJing Liu VirtioCcwDevice *_dev = to_virtio_ccw_dev_fast(dev); 1171a5cf2bb4SCornelia Huck 11720b81c1efSPaolo Bonzini virtio_ccw_stop_ioeventfd(_dev); 1173a5cf2bb4SCornelia Huck } 1174a5cf2bb4SCornelia Huck 1175a5cf2bb4SCornelia Huck static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) 1176a5cf2bb4SCornelia Huck { 1177a5cf2bb4SCornelia Huck DeviceClass *dc = DEVICE_CLASS(klass); 1178b804e8a6SJing Liu CCWDeviceClass *k = CCW_DEVICE_CLASS(dc); 1179838fb84fSCornelia Huck VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_CLASS(klass); 1180a5cf2bb4SCornelia Huck 1181b804e8a6SJing Liu k->unplug = virtio_ccw_busdev_unplug; 11825e5ced38SMarkus Armbruster dc->realize = virtio_ccw_busdev_realize; 118324118af8SNia Alarie dc->unrealize = virtio_ccw_busdev_unrealize; 1184a5cf2bb4SCornelia Huck dc->bus_type = TYPE_VIRTUAL_CSS_BUS; 1185838fb84fSCornelia Huck device_class_set_parent_reset(dc, virtio_ccw_reset, &vdc->parent_reset); 1186a5cf2bb4SCornelia Huck } 1187a5cf2bb4SCornelia Huck 1188a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_device_info = { 1189a5cf2bb4SCornelia Huck .name = TYPE_VIRTIO_CCW_DEVICE, 1190b804e8a6SJing Liu .parent = TYPE_CCW_DEVICE, 1191a5cf2bb4SCornelia Huck .instance_size = sizeof(VirtioCcwDevice), 1192a5cf2bb4SCornelia Huck .class_init = virtio_ccw_device_class_init, 1193a5cf2bb4SCornelia Huck .class_size = sizeof(VirtIOCCWDeviceClass), 1194a5cf2bb4SCornelia Huck .abstract = true, 1195a5cf2bb4SCornelia Huck }; 1196a5cf2bb4SCornelia Huck 1197a5cf2bb4SCornelia Huck /* virtio-ccw-bus */ 1198a5cf2bb4SCornelia Huck 11991bf4d7aaSAndreas Färber static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, 12001bf4d7aaSAndreas Färber VirtioCcwDevice *dev) 1201a5cf2bb4SCornelia Huck { 1202a5cf2bb4SCornelia Huck DeviceState *qdev = DEVICE(dev); 1203f4dd69aaSKONRAD Frederic char virtio_bus_name[] = "virtio-bus"; 1204a5cf2bb4SCornelia Huck 1205fb17dfe0SAndreas Färber qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_CCW_BUS, 1206fb17dfe0SAndreas Färber qdev, virtio_bus_name); 1207a5cf2bb4SCornelia Huck } 1208a5cf2bb4SCornelia Huck 1209a5cf2bb4SCornelia Huck static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) 1210a5cf2bb4SCornelia Huck { 1211a5cf2bb4SCornelia Huck VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); 1212a5cf2bb4SCornelia Huck BusClass *bus_class = BUS_CLASS(klass); 1213a5cf2bb4SCornelia Huck 1214a5cf2bb4SCornelia Huck bus_class->max_dev = 1; 1215a5cf2bb4SCornelia Huck k->notify = virtio_ccw_notify; 1216b4436a0bSCornelia Huck k->vmstate_change = virtio_ccw_vmstate_change; 1217320ce850SCornelia Huck k->query_guest_notifiers = virtio_ccw_query_guest_notifiers; 1218320ce850SCornelia Huck k->set_guest_notifiers = virtio_ccw_set_guest_notifiers; 1219bcb2b582SJens Freimann k->save_queue = virtio_ccw_save_queue; 1220bcb2b582SJens Freimann k->load_queue = virtio_ccw_load_queue; 1221bcb2b582SJens Freimann k->save_config = virtio_ccw_save_config; 1222bcb2b582SJens Freimann k->load_config = virtio_ccw_load_config; 1223d1b4259fSMaxime Coquelin k->pre_plugged = virtio_ccw_pre_plugged; 1224fb846a09SCornelia Huck k->device_plugged = virtio_ccw_device_plugged; 1225fb846a09SCornelia Huck k->device_unplugged = virtio_ccw_device_unplugged; 12268e93cef1SPaolo Bonzini k->ioeventfd_enabled = virtio_ccw_ioeventfd_enabled; 12277c55f68aSCornelia Huck k->ioeventfd_assign = virtio_ccw_ioeventfd_assign; 1228a5cf2bb4SCornelia Huck } 1229a5cf2bb4SCornelia Huck 1230a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_bus_info = { 1231a5cf2bb4SCornelia Huck .name = TYPE_VIRTIO_CCW_BUS, 1232a5cf2bb4SCornelia Huck .parent = TYPE_VIRTIO_BUS, 1233a5cf2bb4SCornelia Huck .instance_size = sizeof(VirtioCcwBusState), 1234a5cf2bb4SCornelia Huck .class_init = virtio_ccw_bus_class_init, 1235a5cf2bb4SCornelia Huck }; 1236a5cf2bb4SCornelia Huck 1237a5cf2bb4SCornelia Huck static void virtio_ccw_register(void) 1238a5cf2bb4SCornelia Huck { 1239a5cf2bb4SCornelia Huck type_register_static(&virtio_ccw_bus_info); 1240a5cf2bb4SCornelia Huck type_register_static(&virtio_ccw_device_info); 1241a5cf2bb4SCornelia Huck } 1242a5cf2bb4SCornelia Huck 1243a5cf2bb4SCornelia Huck type_init(virtio_ccw_register) 1244