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