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 755*b69c3c21SMarkus 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) { 762*b69c3c21SMarkus 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 { 7897e749462SCornelia Huck uint8_t ind_old, ind_new; 7907e749462SCornelia Huck hwaddr len = 1; 7917e749462SCornelia Huck uint8_t *ind_addr; 7927e749462SCornelia Huck 79385eb7c18SPhilippe Mathieu-Daudé ind_addr = cpu_physical_memory_map(ind_loc, &len, true); 7947e749462SCornelia Huck if (!ind_addr) { 7957e749462SCornelia Huck error_report("%s(%x.%x.%04x): unable to access indicator", 7967e749462SCornelia Huck __func__, sch->cssid, sch->ssid, sch->schid); 7977e749462SCornelia Huck return -1; 7987e749462SCornelia Huck } 7997e749462SCornelia Huck do { 8007e749462SCornelia Huck ind_old = *ind_addr; 8017e749462SCornelia Huck ind_new = ind_old | to_be_set; 8027e749462SCornelia Huck } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old); 80306409bd9SChristian Borntraeger trace_virtio_ccw_set_ind(ind_loc, ind_old, ind_new); 8047e749462SCornelia Huck cpu_physical_memory_unmap(ind_addr, len, 1, len); 8057e749462SCornelia Huck 8067e749462SCornelia Huck return ind_old; 8077e749462SCornelia Huck } 8087e749462SCornelia Huck 809a5cf2bb4SCornelia Huck static void virtio_ccw_notify(DeviceState *d, uint16_t vector) 810a5cf2bb4SCornelia Huck { 811a5cf2bb4SCornelia Huck VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); 812b804e8a6SJing Liu CcwDevice *ccw_dev = to_ccw_dev_fast(d); 813b804e8a6SJing Liu SubchDev *sch = ccw_dev->sch; 814a5cf2bb4SCornelia Huck uint64_t indicators; 815a5cf2bb4SCornelia Huck 8167a5342e7SHalil Pasic if (vector == VIRTIO_NO_VECTOR) { 817a5cf2bb4SCornelia Huck return; 818a5cf2bb4SCornelia Huck } 8197a5342e7SHalil Pasic /* 8207a5342e7SHalil Pasic * vector < VIRTIO_QUEUE_MAX: notification for a virtqueue 8217a5342e7SHalil Pasic * vector == VIRTIO_QUEUE_MAX: configuration change notification 8227a5342e7SHalil Pasic * bits beyond that are unused and should never be notified for 8237a5342e7SHalil Pasic */ 8247a5342e7SHalil Pasic assert(vector <= VIRTIO_QUEUE_MAX); 825a5cf2bb4SCornelia Huck 826b1914b82SHalil Pasic if (vector < VIRTIO_QUEUE_MAX) { 8277c486976SCornelia Huck if (!dev->indicators) { 8287c486976SCornelia Huck return; 8297c486976SCornelia Huck } 8307e749462SCornelia Huck if (sch->thinint_active) { 8317e749462SCornelia Huck /* 8327e749462SCornelia Huck * In the adapter interrupt case, indicators points to a 8337e749462SCornelia Huck * memory area that may be (way) larger than 64 bit and 8347e749462SCornelia Huck * ind_bit indicates the start of the indicators in a big 8357e749462SCornelia Huck * endian notation. 8367e749462SCornelia Huck */ 837d426d9fbSCornelia Huck uint64_t ind_bit = dev->routes.adapter.ind_offset; 838d426d9fbSCornelia Huck 8397bca3892SCornelia Huck virtio_set_ind_atomic(sch, dev->indicators->addr + 840d426d9fbSCornelia Huck (ind_bit + vector) / 8, 841d426d9fbSCornelia Huck 0x80 >> ((ind_bit + vector) % 8)); 8427bca3892SCornelia Huck if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr, 8437e749462SCornelia Huck 0x01)) { 84425a08b8dSYi Min Zhao css_adapter_interrupt(CSS_IO_ADAPTER_VIRTIO, dev->thinint_isc); 8457e749462SCornelia Huck } 8467e749462SCornelia Huck } else { 8477a5342e7SHalil Pasic assert(vector < NR_CLASSIC_INDICATOR_BITS); 84842874d3aSPeter Maydell indicators = address_space_ldq(&address_space_memory, 84942874d3aSPeter Maydell dev->indicators->addr, 85042874d3aSPeter Maydell MEMTXATTRS_UNSPECIFIED, 85142874d3aSPeter Maydell NULL); 85219380b1bSCornelia Huck indicators |= 1ULL << vector; 85342874d3aSPeter Maydell address_space_stq(&address_space_memory, dev->indicators->addr, 85442874d3aSPeter Maydell indicators, MEMTXATTRS_UNSPECIFIED, NULL); 8557e749462SCornelia Huck css_conditional_io_interrupt(sch); 8567e749462SCornelia Huck } 857a5cf2bb4SCornelia Huck } else { 8587c486976SCornelia Huck if (!dev->indicators2) { 8597c486976SCornelia Huck return; 8607c486976SCornelia Huck } 86142874d3aSPeter Maydell indicators = address_space_ldq(&address_space_memory, 86242874d3aSPeter Maydell dev->indicators2->addr, 86342874d3aSPeter Maydell MEMTXATTRS_UNSPECIFIED, 86442874d3aSPeter Maydell NULL); 8657a5342e7SHalil Pasic indicators |= 1ULL; 86642874d3aSPeter Maydell address_space_stq(&address_space_memory, dev->indicators2->addr, 86742874d3aSPeter Maydell indicators, MEMTXATTRS_UNSPECIFIED, NULL); 868a5cf2bb4SCornelia Huck css_conditional_io_interrupt(sch); 8697e749462SCornelia Huck } 870a5cf2bb4SCornelia Huck } 871a5cf2bb4SCornelia Huck 872a5cf2bb4SCornelia Huck static void virtio_ccw_reset(DeviceState *d) 873a5cf2bb4SCornelia Huck { 874a5cf2bb4SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 875f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 876838fb84fSCornelia Huck VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); 877a5cf2bb4SCornelia Huck 878fa8b0ca5SCornelia Huck virtio_ccw_reset_virtio(dev, vdev); 879838fb84fSCornelia Huck if (vdc->parent_reset) { 880838fb84fSCornelia Huck vdc->parent_reset(d); 881838fb84fSCornelia Huck } 882a5cf2bb4SCornelia Huck } 883a5cf2bb4SCornelia Huck 884b4436a0bSCornelia Huck static void virtio_ccw_vmstate_change(DeviceState *d, bool running) 885b4436a0bSCornelia Huck { 886b4436a0bSCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 887b4436a0bSCornelia Huck 888b4436a0bSCornelia Huck if (running) { 889b4436a0bSCornelia Huck virtio_ccw_start_ioeventfd(dev); 890b4436a0bSCornelia Huck } else { 891b4436a0bSCornelia Huck virtio_ccw_stop_ioeventfd(dev); 892b4436a0bSCornelia Huck } 893b4436a0bSCornelia Huck } 894b4436a0bSCornelia Huck 895320ce850SCornelia Huck static bool virtio_ccw_query_guest_notifiers(DeviceState *d) 896320ce850SCornelia Huck { 897b804e8a6SJing Liu CcwDevice *dev = CCW_DEVICE(d); 898320ce850SCornelia Huck 899320ce850SCornelia Huck return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA); 900320ce850SCornelia Huck } 901320ce850SCornelia Huck 902d426d9fbSCornelia Huck static int virtio_ccw_get_mappings(VirtioCcwDevice *dev) 903d426d9fbSCornelia Huck { 904d426d9fbSCornelia Huck int r; 905b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev); 906d426d9fbSCornelia Huck 907b804e8a6SJing Liu if (!ccw_dev->sch->thinint_active) { 908d426d9fbSCornelia Huck return -EINVAL; 909d426d9fbSCornelia Huck } 910d426d9fbSCornelia Huck 911d426d9fbSCornelia Huck r = map_indicator(&dev->routes.adapter, dev->summary_indicator); 912d426d9fbSCornelia Huck if (r) { 913d426d9fbSCornelia Huck return r; 914d426d9fbSCornelia Huck } 915d426d9fbSCornelia Huck r = map_indicator(&dev->routes.adapter, dev->indicators); 916d426d9fbSCornelia Huck if (r) { 917d426d9fbSCornelia Huck return r; 918d426d9fbSCornelia Huck } 919d426d9fbSCornelia Huck dev->routes.adapter.summary_addr = dev->summary_indicator->map; 920d426d9fbSCornelia Huck dev->routes.adapter.ind_addr = dev->indicators->map; 921d426d9fbSCornelia Huck 922d426d9fbSCornelia Huck return 0; 923d426d9fbSCornelia Huck } 924d426d9fbSCornelia Huck 925d426d9fbSCornelia Huck static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs) 926d426d9fbSCornelia Huck { 927d426d9fbSCornelia Huck int i; 928d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 929d426d9fbSCornelia Huck int ret; 930d426d9fbSCornelia Huck S390FLICState *fs = s390_get_flic(); 9316762808fSDavid Hildenbrand S390FLICStateClass *fsc = s390_get_flic_class(fs); 932d426d9fbSCornelia Huck 933d426d9fbSCornelia Huck ret = virtio_ccw_get_mappings(dev); 934d426d9fbSCornelia Huck if (ret) { 935d426d9fbSCornelia Huck return ret; 936d426d9fbSCornelia Huck } 937d426d9fbSCornelia Huck for (i = 0; i < nvqs; i++) { 938d426d9fbSCornelia Huck if (!virtio_queue_get_num(vdev, i)) { 939d426d9fbSCornelia Huck break; 940d426d9fbSCornelia Huck } 941d426d9fbSCornelia Huck } 942d426d9fbSCornelia Huck dev->routes.num_routes = i; 943d426d9fbSCornelia Huck return fsc->add_adapter_routes(fs, &dev->routes); 944d426d9fbSCornelia Huck } 945d426d9fbSCornelia Huck 946d426d9fbSCornelia Huck static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs) 947d426d9fbSCornelia Huck { 948d426d9fbSCornelia Huck S390FLICState *fs = s390_get_flic(); 9496762808fSDavid Hildenbrand S390FLICStateClass *fsc = s390_get_flic_class(fs); 950d426d9fbSCornelia Huck 951d426d9fbSCornelia Huck fsc->release_adapter_routes(fs, &dev->routes); 952d426d9fbSCornelia Huck } 953d426d9fbSCornelia Huck 954d426d9fbSCornelia Huck static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n) 955d426d9fbSCornelia Huck { 956d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 957d426d9fbSCornelia Huck VirtQueue *vq = virtio_get_queue(vdev, n); 958d426d9fbSCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); 959d426d9fbSCornelia Huck 9601c9b71a7SEric Auger return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, notifier, NULL, 961d426d9fbSCornelia Huck dev->routes.gsi[n]); 962d426d9fbSCornelia Huck } 963d426d9fbSCornelia Huck 964d426d9fbSCornelia Huck static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n) 965d426d9fbSCornelia Huck { 966d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 967d426d9fbSCornelia Huck VirtQueue *vq = virtio_get_queue(vdev, n); 968d426d9fbSCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); 969d426d9fbSCornelia Huck int ret; 970d426d9fbSCornelia Huck 9711c9b71a7SEric Auger ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, notifier, 972d426d9fbSCornelia Huck dev->routes.gsi[n]); 973d426d9fbSCornelia Huck assert(ret == 0); 974d426d9fbSCornelia Huck } 975d426d9fbSCornelia Huck 976320ce850SCornelia Huck static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, 977320ce850SCornelia Huck bool assign, bool with_irqfd) 978320ce850SCornelia Huck { 979f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 980f24a6840SPaolo Bonzini VirtQueue *vq = virtio_get_queue(vdev, n); 981320ce850SCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); 982f24a6840SPaolo Bonzini VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); 983320ce850SCornelia Huck 984320ce850SCornelia Huck if (assign) { 985320ce850SCornelia Huck int r = event_notifier_init(notifier, 0); 986320ce850SCornelia Huck 987320ce850SCornelia Huck if (r < 0) { 988320ce850SCornelia Huck return r; 989320ce850SCornelia Huck } 990320ce850SCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); 991d426d9fbSCornelia Huck if (with_irqfd) { 992d426d9fbSCornelia Huck r = virtio_ccw_add_irqfd(dev, n); 993d426d9fbSCornelia Huck if (r) { 994d426d9fbSCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, false, 995d426d9fbSCornelia Huck with_irqfd); 996d426d9fbSCornelia Huck return r; 997d426d9fbSCornelia Huck } 998d426d9fbSCornelia Huck } 999d426d9fbSCornelia Huck /* 1000d426d9fbSCornelia Huck * We do not support individual masking for channel devices, so we 1001d426d9fbSCornelia Huck * need to manually trigger any guest masking callbacks here. 1002320ce850SCornelia Huck */ 10032858bc68SWei Huang if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) { 1004f24a6840SPaolo Bonzini k->guest_notifier_mask(vdev, n, false); 1005320ce850SCornelia Huck } 1006320ce850SCornelia Huck /* get lost events and re-inject */ 1007320ce850SCornelia Huck if (k->guest_notifier_pending && 1008f24a6840SPaolo Bonzini k->guest_notifier_pending(vdev, n)) { 1009320ce850SCornelia Huck event_notifier_set(notifier); 1010320ce850SCornelia Huck } 1011320ce850SCornelia Huck } else { 10122858bc68SWei Huang if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) { 1013f24a6840SPaolo Bonzini k->guest_notifier_mask(vdev, n, true); 1014320ce850SCornelia Huck } 1015d426d9fbSCornelia Huck if (with_irqfd) { 1016d426d9fbSCornelia Huck virtio_ccw_remove_irqfd(dev, n); 1017d426d9fbSCornelia Huck } 1018320ce850SCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); 1019320ce850SCornelia Huck event_notifier_cleanup(notifier); 1020320ce850SCornelia Huck } 1021320ce850SCornelia Huck return 0; 1022320ce850SCornelia Huck } 1023320ce850SCornelia Huck 1024320ce850SCornelia Huck static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs, 1025320ce850SCornelia Huck bool assigned) 1026320ce850SCornelia Huck { 1027320ce850SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1028f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1029b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(d); 1030b804e8a6SJing Liu bool with_irqfd = ccw_dev->sch->thinint_active && kvm_irqfds_enabled(); 1031320ce850SCornelia Huck int r, n; 1032320ce850SCornelia Huck 1033d426d9fbSCornelia Huck if (with_irqfd && assigned) { 1034d426d9fbSCornelia Huck /* irq routes need to be set up before assigning irqfds */ 1035d426d9fbSCornelia Huck r = virtio_ccw_setup_irqroutes(dev, nvqs); 1036d426d9fbSCornelia Huck if (r < 0) { 1037d426d9fbSCornelia Huck goto irqroute_error; 1038d426d9fbSCornelia Huck } 1039d426d9fbSCornelia Huck } 1040320ce850SCornelia Huck for (n = 0; n < nvqs; n++) { 1041320ce850SCornelia Huck if (!virtio_queue_get_num(vdev, n)) { 1042320ce850SCornelia Huck break; 1043320ce850SCornelia Huck } 1044d426d9fbSCornelia Huck r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd); 1045320ce850SCornelia Huck if (r < 0) { 1046320ce850SCornelia Huck goto assign_error; 1047320ce850SCornelia Huck } 1048320ce850SCornelia Huck } 1049d426d9fbSCornelia Huck if (with_irqfd && !assigned) { 1050d426d9fbSCornelia Huck /* release irq routes after irqfds have been released */ 1051d426d9fbSCornelia Huck virtio_ccw_release_irqroutes(dev, nvqs); 1052d426d9fbSCornelia Huck } 1053320ce850SCornelia Huck return 0; 1054320ce850SCornelia Huck 1055320ce850SCornelia Huck assign_error: 1056320ce850SCornelia Huck while (--n >= 0) { 1057320ce850SCornelia Huck virtio_ccw_set_guest_notifier(dev, n, !assigned, false); 1058320ce850SCornelia Huck } 1059d426d9fbSCornelia Huck irqroute_error: 1060d426d9fbSCornelia Huck if (with_irqfd && assigned) { 1061d426d9fbSCornelia Huck virtio_ccw_release_irqroutes(dev, nvqs); 1062d426d9fbSCornelia Huck } 1063320ce850SCornelia Huck return r; 1064320ce850SCornelia Huck } 1065320ce850SCornelia Huck 1066bcb2b582SJens Freimann static void virtio_ccw_save_queue(DeviceState *d, int n, QEMUFile *f) 1067bcb2b582SJens Freimann { 1068bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1069bcb2b582SJens Freimann VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1070bcb2b582SJens Freimann 1071bcb2b582SJens Freimann qemu_put_be16(f, virtio_queue_vector(vdev, n)); 1072bcb2b582SJens Freimann } 1073bcb2b582SJens Freimann 1074bcb2b582SJens Freimann static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f) 1075bcb2b582SJens Freimann { 1076bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1077bcb2b582SJens Freimann VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1078bcb2b582SJens Freimann uint16_t vector; 1079bcb2b582SJens Freimann 1080bcb2b582SJens Freimann qemu_get_be16s(f, &vector); 1081bcb2b582SJens Freimann virtio_queue_set_vector(vdev, n , vector); 1082bcb2b582SJens Freimann 1083bcb2b582SJens Freimann return 0; 1084bcb2b582SJens Freimann } 1085bcb2b582SJens Freimann 1086bcb2b582SJens Freimann static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) 1087bcb2b582SJens Freimann { 1088bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1089517ff12cSHalil Pasic vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL); 1090bcb2b582SJens Freimann } 1091bcb2b582SJens Freimann 1092bcb2b582SJens Freimann static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) 1093bcb2b582SJens Freimann { 1094bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1095517ff12cSHalil Pasic return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1); 1096bcb2b582SJens Freimann } 1097bcb2b582SJens Freimann 1098d1b4259fSMaxime Coquelin static void virtio_ccw_pre_plugged(DeviceState *d, Error **errp) 1099d1b4259fSMaxime Coquelin { 1100d1b4259fSMaxime Coquelin VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1101d1b4259fSMaxime Coquelin VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1102d1b4259fSMaxime Coquelin 1103d1b4259fSMaxime Coquelin if (dev->max_rev >= 1) { 1104d1b4259fSMaxime Coquelin virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1); 1105d1b4259fSMaxime Coquelin } 1106d1b4259fSMaxime Coquelin } 1107d1b4259fSMaxime Coquelin 1108fb846a09SCornelia Huck /* This is called by virtio-bus just after the device is plugged. */ 1109e8398045SJason Wang static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) 1110fb846a09SCornelia Huck { 1111fb846a09SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 111210ceaa1eSJason Wang VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); 1113b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(d); 1114b804e8a6SJing Liu SubchDev *sch = ccw_dev->sch; 111510ceaa1eSJason Wang int n = virtio_get_num_queues(vdev); 11160708afa7SHalil Pasic S390FLICState *flic = s390_get_flic(); 111710ceaa1eSJason Wang 1118d1b4259fSMaxime Coquelin if (!virtio_has_feature(vdev->host_features, VIRTIO_F_VERSION_1)) { 1119d1b4259fSMaxime Coquelin dev->max_rev = 0; 1120d1b4259fSMaxime Coquelin } 1121d1b4259fSMaxime Coquelin 1122b1914b82SHalil Pasic if (virtio_get_num_queues(vdev) > VIRTIO_QUEUE_MAX) { 1123b34aee54SMichael Tokarev error_setg(errp, "The number of virtqueues %d " 1124b1914b82SHalil Pasic "exceeds virtio limit %d", n, 1125b1914b82SHalil Pasic VIRTIO_QUEUE_MAX); 112610ceaa1eSJason Wang return; 112710ceaa1eSJason Wang } 11280708afa7SHalil Pasic if (virtio_get_num_queues(vdev) > flic->adapter_routes_max_batch) { 11290708afa7SHalil Pasic error_setg(errp, "The number of virtqueues %d " 11300708afa7SHalil Pasic "exceeds flic adapter route limit %d", n, 11310708afa7SHalil Pasic flic->adapter_routes_max_batch); 11320708afa7SHalil Pasic return; 11330708afa7SHalil Pasic } 1134fb846a09SCornelia Huck 1135fb846a09SCornelia Huck sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus); 1136fb846a09SCornelia Huck 1137542571d5SCornelia Huck 1138fb846a09SCornelia Huck css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1139fb846a09SCornelia Huck d->hotplugged, 1); 1140fb846a09SCornelia Huck } 1141fb846a09SCornelia Huck 1142fb846a09SCornelia Huck static void virtio_ccw_device_unplugged(DeviceState *d) 1143fb846a09SCornelia Huck { 1144fb846a09SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); 1145fb846a09SCornelia Huck 1146fb846a09SCornelia Huck virtio_ccw_stop_ioeventfd(dev); 1147fb846a09SCornelia Huck } 1148a5cf2bb4SCornelia Huck /**************** Virtio-ccw Bus Device Descriptions *******************/ 1149a5cf2bb4SCornelia Huck 11505e5ced38SMarkus Armbruster static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp) 1151a5cf2bb4SCornelia Huck { 1152a5cf2bb4SCornelia Huck VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; 1153a5cf2bb4SCornelia Huck 11541bf4d7aaSAndreas Färber virtio_ccw_bus_new(&_dev->bus, sizeof(_dev->bus), _dev); 11551fa75523SCornelia Huck virtio_ccw_device_realize(_dev, errp); 1156a5cf2bb4SCornelia Huck } 1157a5cf2bb4SCornelia Huck 1158*b69c3c21SMarkus Armbruster static void virtio_ccw_busdev_unrealize(DeviceState *dev) 1159a5cf2bb4SCornelia Huck { 1160a5cf2bb4SCornelia Huck VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; 1161a5cf2bb4SCornelia Huck 1162*b69c3c21SMarkus Armbruster virtio_ccw_device_unrealize(_dev); 1163a5cf2bb4SCornelia Huck } 1164a5cf2bb4SCornelia Huck 1165b804e8a6SJing Liu static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, 1166277bc95eSIgor Mammedov DeviceState *dev, Error **errp) 1167a5cf2bb4SCornelia Huck { 1168b804e8a6SJing Liu VirtioCcwDevice *_dev = to_virtio_ccw_dev_fast(dev); 1169a5cf2bb4SCornelia Huck 11700b81c1efSPaolo Bonzini virtio_ccw_stop_ioeventfd(_dev); 1171a5cf2bb4SCornelia Huck } 1172a5cf2bb4SCornelia Huck 1173a5cf2bb4SCornelia Huck static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) 1174a5cf2bb4SCornelia Huck { 1175a5cf2bb4SCornelia Huck DeviceClass *dc = DEVICE_CLASS(klass); 1176b804e8a6SJing Liu CCWDeviceClass *k = CCW_DEVICE_CLASS(dc); 1177838fb84fSCornelia Huck VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_CLASS(klass); 1178a5cf2bb4SCornelia Huck 1179b804e8a6SJing Liu k->unplug = virtio_ccw_busdev_unplug; 11805e5ced38SMarkus Armbruster dc->realize = virtio_ccw_busdev_realize; 118124118af8SNia Alarie dc->unrealize = virtio_ccw_busdev_unrealize; 1182a5cf2bb4SCornelia Huck dc->bus_type = TYPE_VIRTUAL_CSS_BUS; 1183838fb84fSCornelia Huck device_class_set_parent_reset(dc, virtio_ccw_reset, &vdc->parent_reset); 1184a5cf2bb4SCornelia Huck } 1185a5cf2bb4SCornelia Huck 1186a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_device_info = { 1187a5cf2bb4SCornelia Huck .name = TYPE_VIRTIO_CCW_DEVICE, 1188b804e8a6SJing Liu .parent = TYPE_CCW_DEVICE, 1189a5cf2bb4SCornelia Huck .instance_size = sizeof(VirtioCcwDevice), 1190a5cf2bb4SCornelia Huck .class_init = virtio_ccw_device_class_init, 1191a5cf2bb4SCornelia Huck .class_size = sizeof(VirtIOCCWDeviceClass), 1192a5cf2bb4SCornelia Huck .abstract = true, 1193a5cf2bb4SCornelia Huck }; 1194a5cf2bb4SCornelia Huck 1195a5cf2bb4SCornelia Huck /* virtio-ccw-bus */ 1196a5cf2bb4SCornelia Huck 11971bf4d7aaSAndreas Färber static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, 11981bf4d7aaSAndreas Färber VirtioCcwDevice *dev) 1199a5cf2bb4SCornelia Huck { 1200a5cf2bb4SCornelia Huck DeviceState *qdev = DEVICE(dev); 1201f4dd69aaSKONRAD Frederic char virtio_bus_name[] = "virtio-bus"; 1202a5cf2bb4SCornelia Huck 1203fb17dfe0SAndreas Färber qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_CCW_BUS, 1204fb17dfe0SAndreas Färber qdev, virtio_bus_name); 1205a5cf2bb4SCornelia Huck } 1206a5cf2bb4SCornelia Huck 1207a5cf2bb4SCornelia Huck static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) 1208a5cf2bb4SCornelia Huck { 1209a5cf2bb4SCornelia Huck VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); 1210a5cf2bb4SCornelia Huck BusClass *bus_class = BUS_CLASS(klass); 1211a5cf2bb4SCornelia Huck 1212a5cf2bb4SCornelia Huck bus_class->max_dev = 1; 1213a5cf2bb4SCornelia Huck k->notify = virtio_ccw_notify; 1214b4436a0bSCornelia Huck k->vmstate_change = virtio_ccw_vmstate_change; 1215320ce850SCornelia Huck k->query_guest_notifiers = virtio_ccw_query_guest_notifiers; 1216320ce850SCornelia Huck k->set_guest_notifiers = virtio_ccw_set_guest_notifiers; 1217bcb2b582SJens Freimann k->save_queue = virtio_ccw_save_queue; 1218bcb2b582SJens Freimann k->load_queue = virtio_ccw_load_queue; 1219bcb2b582SJens Freimann k->save_config = virtio_ccw_save_config; 1220bcb2b582SJens Freimann k->load_config = virtio_ccw_load_config; 1221d1b4259fSMaxime Coquelin k->pre_plugged = virtio_ccw_pre_plugged; 1222fb846a09SCornelia Huck k->device_plugged = virtio_ccw_device_plugged; 1223fb846a09SCornelia Huck k->device_unplugged = virtio_ccw_device_unplugged; 12248e93cef1SPaolo Bonzini k->ioeventfd_enabled = virtio_ccw_ioeventfd_enabled; 12257c55f68aSCornelia Huck k->ioeventfd_assign = virtio_ccw_ioeventfd_assign; 1226a5cf2bb4SCornelia Huck } 1227a5cf2bb4SCornelia Huck 1228a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_bus_info = { 1229a5cf2bb4SCornelia Huck .name = TYPE_VIRTIO_CCW_BUS, 1230a5cf2bb4SCornelia Huck .parent = TYPE_VIRTIO_BUS, 1231a5cf2bb4SCornelia Huck .instance_size = sizeof(VirtioCcwBusState), 1232a5cf2bb4SCornelia Huck .class_init = virtio_ccw_bus_class_init, 1233a5cf2bb4SCornelia Huck }; 1234a5cf2bb4SCornelia Huck 1235a5cf2bb4SCornelia Huck static void virtio_ccw_register(void) 1236a5cf2bb4SCornelia Huck { 1237a5cf2bb4SCornelia Huck type_register_static(&virtio_ccw_bus_info); 1238a5cf2bb4SCornelia Huck type_register_static(&virtio_ccw_device_info); 1239a5cf2bb4SCornelia Huck } 1240a5cf2bb4SCornelia Huck 1241a5cf2bb4SCornelia Huck type_init(virtio_ccw_register) 1242