xref: /qemu/hw/s390x/virtio-ccw.c (revision b9b59a36)
1a5cf2bb4SCornelia Huck /*
2a5cf2bb4SCornelia Huck  * virtio ccw target implementation
3a5cf2bb4SCornelia Huck  *
4de6a9218SPierre Morel  * Copyright 2012,2015 IBM Corp.
5a5cf2bb4SCornelia Huck  * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
6de6a9218SPierre Morel  *            Pierre Morel <pmorel@linux.vnet.ibm.com>
7a5cf2bb4SCornelia Huck  *
8a5cf2bb4SCornelia Huck  * This work is licensed under the terms of the GNU GPL, version 2 or (at
9a5cf2bb4SCornelia Huck  * your option) any later version. See the COPYING file in the top-level
10a5cf2bb4SCornelia Huck  * directory.
11a5cf2bb4SCornelia Huck  */
12a5cf2bb4SCornelia Huck 
139615495aSPeter Maydell #include "qemu/osdep.h"
14da34e65cSMarkus Armbruster #include "qapi/error.h"
15a43de798SPaolo Bonzini #include "exec/address-spaces.h"
16bd3f16acSPaolo Bonzini #include "sysemu/kvm.h"
17a5cf2bb4SCornelia Huck #include "net/net.h"
180d09e41aSPaolo Bonzini #include "hw/virtio/virtio.h"
19ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h"
200d09e41aSPaolo Bonzini #include "hw/virtio/virtio-net.h"
21a5cf2bb4SCornelia Huck #include "qemu/bitops.h"
22d49b6836SMarkus Armbruster #include "qemu/error-report.h"
23a43de798SPaolo Bonzini #include "qemu/log.h"
240b8fa32fSMarkus Armbruster #include "qemu/module.h"
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"
353909c079SPavel Dovgalyuk #include "sysemu/replay.h"
36a5cf2bb4SCornelia Huck 
37797b6086SHalil Pasic #define NR_CLASSIC_INDICATOR_BITS 64
38797b6086SHalil Pasic 
392dd9d8cfSGerd Hoffmann bool have_virtio_ccw = true;
402dd9d8cfSGerd Hoffmann 
virtio_ccw_dev_post_load(void * opaque,int version_id)41517ff12cSHalil Pasic static int virtio_ccw_dev_post_load(void *opaque, int version_id)
42517ff12cSHalil Pasic {
43517ff12cSHalil Pasic     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(opaque);
44517ff12cSHalil Pasic     CcwDevice *ccw_dev = CCW_DEVICE(dev);
45517ff12cSHalil Pasic     CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev);
46517ff12cSHalil Pasic 
47517ff12cSHalil Pasic     ccw_dev->sch->driver_data = dev;
48517ff12cSHalil Pasic     if (ccw_dev->sch->thinint_active) {
49517ff12cSHalil Pasic         dev->routes.adapter.adapter_id = css_get_adapter_id(
50517ff12cSHalil Pasic                                          CSS_IO_ADAPTER_VIRTIO,
51517ff12cSHalil Pasic                                          dev->thinint_isc);
52517ff12cSHalil Pasic     }
53517ff12cSHalil Pasic     /* Re-fill subch_id after loading the subchannel states.*/
54517ff12cSHalil Pasic     if (ck->refill_ids) {
55517ff12cSHalil Pasic         ck->refill_ids(ccw_dev);
56517ff12cSHalil Pasic     }
57517ff12cSHalil Pasic     return 0;
58517ff12cSHalil Pasic }
59517ff12cSHalil Pasic 
60517ff12cSHalil Pasic typedef struct VirtioCcwDeviceTmp {
61517ff12cSHalil Pasic     VirtioCcwDevice *parent;
62517ff12cSHalil Pasic     uint16_t config_vector;
63517ff12cSHalil Pasic } VirtioCcwDeviceTmp;
64517ff12cSHalil Pasic 
virtio_ccw_dev_tmp_pre_save(void * opaque)6544b1ff31SDr. David Alan Gilbert static int virtio_ccw_dev_tmp_pre_save(void *opaque)
66517ff12cSHalil Pasic {
67517ff12cSHalil Pasic     VirtioCcwDeviceTmp *tmp = opaque;
68517ff12cSHalil Pasic     VirtioCcwDevice *dev = tmp->parent;
69517ff12cSHalil Pasic     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
70517ff12cSHalil Pasic 
71517ff12cSHalil Pasic     tmp->config_vector = vdev->config_vector;
7244b1ff31SDr. David Alan Gilbert 
7344b1ff31SDr. David Alan Gilbert     return 0;
74517ff12cSHalil Pasic }
75517ff12cSHalil Pasic 
virtio_ccw_dev_tmp_post_load(void * opaque,int version_id)76517ff12cSHalil Pasic static int virtio_ccw_dev_tmp_post_load(void *opaque, int version_id)
77517ff12cSHalil Pasic {
78517ff12cSHalil Pasic     VirtioCcwDeviceTmp *tmp = opaque;
79517ff12cSHalil Pasic     VirtioCcwDevice *dev = tmp->parent;
80517ff12cSHalil Pasic     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
81517ff12cSHalil Pasic 
82517ff12cSHalil Pasic     vdev->config_vector = tmp->config_vector;
83517ff12cSHalil Pasic     return 0;
84517ff12cSHalil Pasic }
85517ff12cSHalil Pasic 
86517ff12cSHalil Pasic const VMStateDescription vmstate_virtio_ccw_dev_tmp = {
87517ff12cSHalil Pasic     .name = "s390_virtio_ccw_dev_tmp",
88517ff12cSHalil Pasic     .pre_save = virtio_ccw_dev_tmp_pre_save,
89517ff12cSHalil Pasic     .post_load = virtio_ccw_dev_tmp_post_load,
90*b9b59a36SRichard Henderson     .fields = (const VMStateField[]) {
91517ff12cSHalil Pasic         VMSTATE_UINT16(config_vector, VirtioCcwDeviceTmp),
92517ff12cSHalil Pasic         VMSTATE_END_OF_LIST()
93517ff12cSHalil Pasic     }
94517ff12cSHalil Pasic };
95517ff12cSHalil Pasic 
96517ff12cSHalil Pasic const VMStateDescription vmstate_virtio_ccw_dev = {
97517ff12cSHalil Pasic     .name = "s390_virtio_ccw_dev",
98517ff12cSHalil Pasic     .version_id = 1,
99517ff12cSHalil Pasic     .minimum_version_id = 1,
100517ff12cSHalil Pasic     .post_load = virtio_ccw_dev_post_load,
101*b9b59a36SRichard Henderson     .fields = (const VMStateField[]) {
102517ff12cSHalil Pasic         VMSTATE_CCW_DEVICE(parent_obj, VirtioCcwDevice),
103517ff12cSHalil Pasic         VMSTATE_PTR_TO_IND_ADDR(indicators, VirtioCcwDevice),
104517ff12cSHalil Pasic         VMSTATE_PTR_TO_IND_ADDR(indicators2, VirtioCcwDevice),
105517ff12cSHalil Pasic         VMSTATE_PTR_TO_IND_ADDR(summary_indicator, VirtioCcwDevice),
106517ff12cSHalil Pasic         /*
107517ff12cSHalil Pasic          * Ugly hack because VirtIODevice does not migrate itself.
108517ff12cSHalil Pasic          * This also makes legacy via vmstate_save_state possible.
109517ff12cSHalil Pasic          */
110517ff12cSHalil Pasic         VMSTATE_WITH_TMP(VirtioCcwDevice, VirtioCcwDeviceTmp,
111517ff12cSHalil Pasic                          vmstate_virtio_ccw_dev_tmp),
112517ff12cSHalil Pasic         VMSTATE_STRUCT(routes, VirtioCcwDevice, 1, vmstate_adapter_routes,
113517ff12cSHalil Pasic                        AdapterRoutes),
114517ff12cSHalil Pasic         VMSTATE_UINT8(thinint_isc, VirtioCcwDevice),
115517ff12cSHalil Pasic         VMSTATE_INT32(revision, VirtioCcwDevice),
116517ff12cSHalil Pasic         VMSTATE_END_OF_LIST()
117517ff12cSHalil Pasic     }
118517ff12cSHalil Pasic };
119517ff12cSHalil Pasic 
1201bf4d7aaSAndreas Färber static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
1211bf4d7aaSAndreas Färber                                VirtioCcwDevice *dev);
122d51fcfacSKONRAD Frederic 
virtio_ccw_get_vdev(SubchDev * sch)123a5cf2bb4SCornelia Huck VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
124a5cf2bb4SCornelia Huck {
125a5cf2bb4SCornelia Huck     VirtIODevice *vdev = NULL;
126f24a6840SPaolo Bonzini     VirtioCcwDevice *dev = sch->driver_data;
127a5cf2bb4SCornelia Huck 
128f24a6840SPaolo Bonzini     if (dev) {
129f24a6840SPaolo Bonzini         vdev = virtio_bus_get_device(&dev->bus);
130a5cf2bb4SCornelia Huck     }
131a5cf2bb4SCornelia Huck     return vdev;
132a5cf2bb4SCornelia Huck }
133a5cf2bb4SCornelia Huck 
virtio_ccw_start_ioeventfd(VirtioCcwDevice * dev)134b4436a0bSCornelia Huck static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
135b4436a0bSCornelia Huck {
1367c55f68aSCornelia Huck     virtio_bus_start_ioeventfd(&dev->bus);
137b4436a0bSCornelia Huck }
138b4436a0bSCornelia Huck 
virtio_ccw_stop_ioeventfd(VirtioCcwDevice * dev)139b4436a0bSCornelia Huck static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev)
140b4436a0bSCornelia Huck {
1417c55f68aSCornelia Huck     virtio_bus_stop_ioeventfd(&dev->bus);
1427c55f68aSCornelia Huck }
143b4436a0bSCornelia Huck 
virtio_ccw_ioeventfd_enabled(DeviceState * d)1448e93cef1SPaolo Bonzini static bool virtio_ccw_ioeventfd_enabled(DeviceState *d)
1457c55f68aSCornelia Huck {
1467c55f68aSCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1477c55f68aSCornelia Huck 
1488e93cef1SPaolo Bonzini     return (dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) != 0;
1497c55f68aSCornelia Huck }
1507c55f68aSCornelia Huck 
virtio_ccw_ioeventfd_assign(DeviceState * d,EventNotifier * notifier,int n,bool assign)1517c55f68aSCornelia Huck static int virtio_ccw_ioeventfd_assign(DeviceState *d, EventNotifier *notifier,
1527c55f68aSCornelia Huck                                        int n, bool assign)
1537c55f68aSCornelia Huck {
1547c55f68aSCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
155b804e8a6SJing Liu     CcwDevice *ccw_dev = CCW_DEVICE(dev);
156b804e8a6SJing Liu     SubchDev *sch = ccw_dev->sch;
1577c55f68aSCornelia Huck     uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid;
1587c55f68aSCornelia Huck 
1597c55f68aSCornelia Huck     return s390_assign_subch_ioeventfd(notifier, sch_id, n, assign);
160b4436a0bSCornelia Huck }
161b4436a0bSCornelia Huck 
162a5cf2bb4SCornelia Huck /* Communication blocks used by several channel commands. */
1630db87e0dSCornelia Huck typedef struct VqInfoBlockLegacy {
164a5cf2bb4SCornelia Huck     uint64_t queue;
165a5cf2bb4SCornelia Huck     uint32_t align;
166a5cf2bb4SCornelia Huck     uint16_t index;
167a5cf2bb4SCornelia Huck     uint16_t num;
1680db87e0dSCornelia Huck } QEMU_PACKED VqInfoBlockLegacy;
1690db87e0dSCornelia Huck 
1700db87e0dSCornelia Huck typedef struct VqInfoBlock {
1710db87e0dSCornelia Huck     uint64_t desc;
1720db87e0dSCornelia Huck     uint32_t res0;
1730db87e0dSCornelia Huck     uint16_t index;
1740db87e0dSCornelia Huck     uint16_t num;
1750db87e0dSCornelia Huck     uint64_t avail;
1760db87e0dSCornelia Huck     uint64_t used;
177a5cf2bb4SCornelia Huck } QEMU_PACKED VqInfoBlock;
178a5cf2bb4SCornelia Huck 
179a5cf2bb4SCornelia Huck typedef struct VqConfigBlock {
180a5cf2bb4SCornelia Huck     uint16_t index;
181a5cf2bb4SCornelia Huck     uint16_t num_max;
182a5cf2bb4SCornelia Huck } QEMU_PACKED VqConfigBlock;
183a5cf2bb4SCornelia Huck 
184a5cf2bb4SCornelia Huck typedef struct VirtioFeatDesc {
185a5cf2bb4SCornelia Huck     uint32_t features;
186a5cf2bb4SCornelia Huck     uint8_t index;
187a5cf2bb4SCornelia Huck } QEMU_PACKED VirtioFeatDesc;
188a5cf2bb4SCornelia Huck 
1897e749462SCornelia Huck typedef struct VirtioThinintInfo {
1907e749462SCornelia Huck     hwaddr summary_indicator;
1917e749462SCornelia Huck     hwaddr device_indicator;
1927e749462SCornelia Huck     uint64_t ind_bit;
1937e749462SCornelia Huck     uint8_t isc;
1947e749462SCornelia Huck } QEMU_PACKED VirtioThinintInfo;
1957e749462SCornelia Huck 
196c42767f2SThomas Huth typedef struct VirtioRevInfo {
197c42767f2SThomas Huth     uint16_t revision;
198c42767f2SThomas Huth     uint16_t length;
199880a7817SPhilippe Mathieu-Daudé     uint8_t data[];
200c42767f2SThomas Huth } QEMU_PACKED VirtioRevInfo;
201c42767f2SThomas Huth 
202a5cf2bb4SCornelia Huck /* Specify where the virtqueues for the subchannel are in guest memory. */
virtio_ccw_set_vqs(SubchDev * sch,VqInfoBlock * info,VqInfoBlockLegacy * linfo)2030db87e0dSCornelia Huck static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info,
2040db87e0dSCornelia Huck                               VqInfoBlockLegacy *linfo)
205a5cf2bb4SCornelia Huck {
206f24a6840SPaolo Bonzini     VirtIODevice *vdev = virtio_ccw_get_vdev(sch);
2070db87e0dSCornelia Huck     uint16_t index = info ? info->index : linfo->index;
2080db87e0dSCornelia Huck     uint16_t num = info ? info->num : linfo->num;
2090db87e0dSCornelia Huck     uint64_t desc = info ? info->desc : linfo->queue;
210a5cf2bb4SCornelia Huck 
211b1914b82SHalil Pasic     if (index >= VIRTIO_QUEUE_MAX) {
212a5cf2bb4SCornelia Huck         return -EINVAL;
213a5cf2bb4SCornelia Huck     }
214a5cf2bb4SCornelia Huck 
215a5cf2bb4SCornelia Huck     /* Current code in virtio.c relies on 4K alignment. */
2160db87e0dSCornelia Huck     if (linfo && desc && (linfo->align != 4096)) {
217a5cf2bb4SCornelia Huck         return -EINVAL;
218a5cf2bb4SCornelia Huck     }
219a5cf2bb4SCornelia Huck 
220f24a6840SPaolo Bonzini     if (!vdev) {
221a5cf2bb4SCornelia Huck         return -EINVAL;
222a5cf2bb4SCornelia Huck     }
223a5cf2bb4SCornelia Huck 
2240db87e0dSCornelia Huck     if (info) {
2250db87e0dSCornelia Huck         virtio_queue_set_rings(vdev, index, desc, info->avail, info->used);
2260db87e0dSCornelia Huck     } else {
2270db87e0dSCornelia Huck         virtio_queue_set_addr(vdev, index, desc);
2280db87e0dSCornelia Huck     }
2290db87e0dSCornelia Huck     if (!desc) {
230955cc8c9SJason Wang         virtio_queue_set_vector(vdev, index, VIRTIO_NO_VECTOR);
231a5cf2bb4SCornelia Huck     } else {
23279cd0c80SCornelia Huck         if (info) {
23379cd0c80SCornelia Huck             /* virtio-1 allows changing the ring size. */
2348c797e75SMichael S. Tsirkin             if (virtio_queue_get_max_num(vdev, index) < num) {
23579cd0c80SCornelia Huck                 /* Fail if we exceed the maximum number. */
236a5cf2bb4SCornelia Huck                 return -EINVAL;
237a5cf2bb4SCornelia Huck             }
23879cd0c80SCornelia Huck             virtio_queue_set_num(vdev, index, num);
239f0d634eaSCarlos López             virtio_init_region_cache(vdev, index);
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 
virtio_ccw_reset_virtio(VirtioCcwDevice * dev)252997340f3SPaolo Bonzini static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev)
253fa8b0ca5SCornelia Huck {
254b804e8a6SJing Liu     CcwDevice *ccw_dev = CCW_DEVICE(dev);
255b804e8a6SJing Liu 
256997340f3SPaolo Bonzini     virtio_bus_reset(&dev->bus);
257fa8b0ca5SCornelia Huck     if (dev->indicators) {
258fa8b0ca5SCornelia Huck         release_indicator(&dev->routes.adapter, dev->indicators);
259fa8b0ca5SCornelia Huck         dev->indicators = NULL;
260fa8b0ca5SCornelia Huck     }
261fa8b0ca5SCornelia Huck     if (dev->indicators2) {
262fa8b0ca5SCornelia Huck         release_indicator(&dev->routes.adapter, dev->indicators2);
263fa8b0ca5SCornelia Huck         dev->indicators2 = NULL;
264fa8b0ca5SCornelia Huck     }
265fa8b0ca5SCornelia Huck     if (dev->summary_indicator) {
266fa8b0ca5SCornelia Huck         release_indicator(&dev->routes.adapter, dev->summary_indicator);
267fa8b0ca5SCornelia Huck         dev->summary_indicator = NULL;
268fa8b0ca5SCornelia Huck     }
269b804e8a6SJing Liu     ccw_dev->sch->thinint_active = false;
270fa8b0ca5SCornelia Huck }
271fa8b0ca5SCornelia Huck 
virtio_ccw_handle_set_vq(SubchDev * sch,CCW1 ccw,bool check_len,bool is_legacy)2720db87e0dSCornelia Huck static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len,
2730db87e0dSCornelia Huck                                     bool is_legacy)
274a5cf2bb4SCornelia Huck {
275a5cf2bb4SCornelia Huck     int ret;
276a5cf2bb4SCornelia Huck     VqInfoBlock info;
2770db87e0dSCornelia Huck     VqInfoBlockLegacy linfo;
2780db87e0dSCornelia Huck     size_t info_len = is_legacy ? sizeof(linfo) : sizeof(info);
2790db87e0dSCornelia Huck 
2800db87e0dSCornelia Huck     if (check_len) {
2810db87e0dSCornelia Huck         if (ccw.count != info_len) {
2820db87e0dSCornelia Huck             return -EINVAL;
2830db87e0dSCornelia Huck         }
2840db87e0dSCornelia Huck     } else if (ccw.count < info_len) {
2850db87e0dSCornelia Huck         /* Can't execute command. */
2860db87e0dSCornelia Huck         return -EINVAL;
2870db87e0dSCornelia Huck     }
2880db87e0dSCornelia Huck     if (!ccw.cda) {
2890db87e0dSCornelia Huck         return -EFAULT;
2900db87e0dSCornelia Huck     }
2910db87e0dSCornelia Huck     if (is_legacy) {
292d895d25aSPierre Morel         ret = ccw_dstream_read(&sch->cds, linfo);
293d895d25aSPierre Morel         if (ret) {
294d895d25aSPierre Morel             return ret;
295d895d25aSPierre Morel         }
296c9aacaadSPeter Maydell         linfo.queue = be64_to_cpu(linfo.queue);
297c9aacaadSPeter Maydell         linfo.align = be32_to_cpu(linfo.align);
298c9aacaadSPeter Maydell         linfo.index = be16_to_cpu(linfo.index);
299c9aacaadSPeter Maydell         linfo.num = be16_to_cpu(linfo.num);
3000db87e0dSCornelia Huck         ret = virtio_ccw_set_vqs(sch, NULL, &linfo);
3010db87e0dSCornelia Huck     } else {
302d895d25aSPierre Morel         ret = ccw_dstream_read(&sch->cds, info);
303d895d25aSPierre Morel         if (ret) {
304d895d25aSPierre Morel             return ret;
305d895d25aSPierre Morel         }
306c9aacaadSPeter Maydell         info.desc = be64_to_cpu(info.desc);
307c9aacaadSPeter Maydell         info.index = be16_to_cpu(info.index);
308c9aacaadSPeter Maydell         info.num = be16_to_cpu(info.num);
309c9aacaadSPeter Maydell         info.avail = be64_to_cpu(info.avail);
310c9aacaadSPeter Maydell         info.used = be64_to_cpu(info.used);
3110db87e0dSCornelia Huck         ret = virtio_ccw_set_vqs(sch, &info, NULL);
3120db87e0dSCornelia Huck     }
3130db87e0dSCornelia Huck     sch->curr_status.scsw.count = 0;
3140db87e0dSCornelia Huck     return ret;
3150db87e0dSCornelia Huck }
3160db87e0dSCornelia Huck 
virtio_ccw_cb(SubchDev * sch,CCW1 ccw)3170db87e0dSCornelia Huck static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
3180db87e0dSCornelia Huck {
3190db87e0dSCornelia Huck     int ret;
320c42767f2SThomas Huth     VirtioRevInfo revinfo;
321a5cf2bb4SCornelia Huck     uint8_t status;
322a5cf2bb4SCornelia Huck     VirtioFeatDesc features;
323a5cf2bb4SCornelia Huck     hwaddr indicators;
324a5cf2bb4SCornelia Huck     VqConfigBlock vq_config;
325a5cf2bb4SCornelia Huck     VirtioCcwDevice *dev = sch->driver_data;
326f24a6840SPaolo Bonzini     VirtIODevice *vdev = virtio_ccw_get_vdev(sch);
327a5cf2bb4SCornelia Huck     bool check_len;
328a5cf2bb4SCornelia Huck     int len;
329f57ba058SHalil Pasic     VirtioThinintInfo thinint;
330a5cf2bb4SCornelia Huck 
331a5cf2bb4SCornelia Huck     if (!dev) {
332a5cf2bb4SCornelia Huck         return -EINVAL;
333a5cf2bb4SCornelia Huck     }
334a5cf2bb4SCornelia Huck 
335a5cf2bb4SCornelia Huck     trace_virtio_ccw_interpret_ccw(sch->cssid, sch->ssid, sch->schid,
336a5cf2bb4SCornelia Huck                                    ccw.cmd_code);
337a5cf2bb4SCornelia Huck     check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC));
338a5cf2bb4SCornelia Huck 
339151fcdfdSCornelia Huck     if (dev->revision < 0 && ccw.cmd_code != CCW_CMD_SET_VIRTIO_REV) {
340151fcdfdSCornelia Huck         if (dev->force_revision_1) {
34147e13dfdSHalil Pasic             /*
34247e13dfdSHalil Pasic              * virtio-1 drivers must start with negotiating to a revision >= 1,
34347e13dfdSHalil Pasic              * so post a command reject for all other commands
34447e13dfdSHalil Pasic              */
34547e13dfdSHalil Pasic             return -ENOSYS;
346151fcdfdSCornelia Huck         } else {
347151fcdfdSCornelia Huck             /*
348151fcdfdSCornelia Huck              * If the driver issues any command that is not SET_VIRTIO_REV,
349151fcdfdSCornelia Huck              * we'll have to operate the device in legacy mode.
350151fcdfdSCornelia Huck              */
351151fcdfdSCornelia Huck             dev->revision = 0;
352151fcdfdSCornelia Huck         }
35347e13dfdSHalil Pasic     }
35447e13dfdSHalil Pasic 
355a5cf2bb4SCornelia Huck     /* Look at the command. */
356a5cf2bb4SCornelia Huck     switch (ccw.cmd_code) {
357a5cf2bb4SCornelia Huck     case CCW_CMD_SET_VQ:
3580db87e0dSCornelia Huck         ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1);
359a5cf2bb4SCornelia Huck         break;
360a5cf2bb4SCornelia Huck     case CCW_CMD_VDEV_RESET:
361997340f3SPaolo Bonzini         virtio_ccw_reset_virtio(dev);
362a5cf2bb4SCornelia Huck         ret = 0;
363a5cf2bb4SCornelia Huck         break;
364a5cf2bb4SCornelia Huck     case CCW_CMD_READ_FEAT:
365a5cf2bb4SCornelia Huck         if (check_len) {
366a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(features)) {
367a5cf2bb4SCornelia Huck                 ret = -EINVAL;
368a5cf2bb4SCornelia Huck                 break;
369a5cf2bb4SCornelia Huck             }
370a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(features)) {
371a5cf2bb4SCornelia Huck             /* Can't execute command. */
372a5cf2bb4SCornelia Huck             ret = -EINVAL;
373a5cf2bb4SCornelia Huck             break;
374a5cf2bb4SCornelia Huck         }
375a5cf2bb4SCornelia Huck         if (!ccw.cda) {
376a5cf2bb4SCornelia Huck             ret = -EFAULT;
377a5cf2bb4SCornelia Huck         } else {
3789b706dbbSMichael S. Tsirkin             VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
3799b706dbbSMichael S. Tsirkin 
380f57ba058SHalil Pasic             ccw_dstream_advance(&sch->cds, sizeof(features.features));
381d895d25aSPierre Morel             ret = ccw_dstream_read(&sch->cds, features.index);
382d895d25aSPierre Morel             if (ret) {
383d895d25aSPierre Morel                 break;
384d895d25aSPierre Morel             }
3856b8f1020SCornelia Huck             if (features.index == 0) {
386542571d5SCornelia Huck                 if (dev->revision >= 1) {
387542571d5SCornelia Huck                     /* Don't offer legacy features for modern devices. */
388542571d5SCornelia Huck                     features.features = (uint32_t)
3899b706dbbSMichael S. Tsirkin                         (vdev->host_features & ~vdc->legacy_features);
390542571d5SCornelia Huck                 } else {
391c42767f2SThomas Huth                     features.features = (uint32_t)vdev->host_features;
392542571d5SCornelia Huck                 }
393b4f8f9dfSCornelia Huck             } else if ((features.index == 1) && (dev->revision >= 1)) {
394c42767f2SThomas Huth                 /*
395b4f8f9dfSCornelia Huck                  * Only offer feature bits beyond 31 if the guest has
396b4f8f9dfSCornelia Huck                  * negotiated at least revision 1.
397c42767f2SThomas Huth                  */
398b4f8f9dfSCornelia Huck                 features.features = (uint32_t)(vdev->host_features >> 32);
399a5cf2bb4SCornelia Huck             } else {
400a5cf2bb4SCornelia Huck                 /* Return zeroes if the guest supports more feature bits. */
401a5cf2bb4SCornelia Huck                 features.features = 0;
402a5cf2bb4SCornelia Huck             }
403f57ba058SHalil Pasic             ccw_dstream_rewind(&sch->cds);
404c9aacaadSPeter Maydell             features.features = cpu_to_le32(features.features);
405d895d25aSPierre Morel             ret = ccw_dstream_write(&sch->cds, features.features);
406d895d25aSPierre Morel             if (!ret) {
407a5cf2bb4SCornelia Huck                 sch->curr_status.scsw.count = ccw.count - sizeof(features);
408d895d25aSPierre Morel             }
409a5cf2bb4SCornelia Huck         }
410a5cf2bb4SCornelia Huck         break;
411a5cf2bb4SCornelia Huck     case CCW_CMD_WRITE_FEAT:
412a5cf2bb4SCornelia Huck         if (check_len) {
413a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(features)) {
414a5cf2bb4SCornelia Huck                 ret = -EINVAL;
415a5cf2bb4SCornelia Huck                 break;
416a5cf2bb4SCornelia Huck             }
417a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(features)) {
418a5cf2bb4SCornelia Huck             /* Can't execute command. */
419a5cf2bb4SCornelia Huck             ret = -EINVAL;
420a5cf2bb4SCornelia Huck             break;
421a5cf2bb4SCornelia Huck         }
422a5cf2bb4SCornelia Huck         if (!ccw.cda) {
423a5cf2bb4SCornelia Huck             ret = -EFAULT;
424a5cf2bb4SCornelia Huck         } else {
425d895d25aSPierre Morel             ret = ccw_dstream_read(&sch->cds, features);
426d895d25aSPierre Morel             if (ret) {
427d895d25aSPierre Morel                 break;
428d895d25aSPierre Morel             }
429c9aacaadSPeter Maydell             features.features = le32_to_cpu(features.features);
4306b8f1020SCornelia Huck             if (features.index == 0) {
431c42767f2SThomas Huth                 virtio_set_features(vdev,
432c42767f2SThomas Huth                                     (vdev->guest_features & 0xffffffff00000000ULL) |
433c42767f2SThomas Huth                                     features.features);
434b4f8f9dfSCornelia Huck             } else if ((features.index == 1) && (dev->revision >= 1)) {
435c42767f2SThomas Huth                 /*
436b4f8f9dfSCornelia Huck                  * If the guest did not negotiate at least revision 1,
437b4f8f9dfSCornelia Huck                  * we did not offer it any feature bits beyond 31. Such a
438b4f8f9dfSCornelia Huck                  * guest passing us any bit here is therefore buggy.
439c42767f2SThomas Huth                  */
440c42767f2SThomas Huth                 virtio_set_features(vdev,
441c42767f2SThomas Huth                                     (vdev->guest_features & 0x00000000ffffffffULL) |
442c42767f2SThomas Huth                                     ((uint64_t)features.features << 32));
443a5cf2bb4SCornelia Huck             } else {
444a5cf2bb4SCornelia Huck                 /*
445a5cf2bb4SCornelia Huck                  * If the guest supports more feature bits, assert that it
446a5cf2bb4SCornelia Huck                  * passes us zeroes for those we don't support.
447a5cf2bb4SCornelia Huck                  */
448a5cf2bb4SCornelia Huck                 if (features.features) {
44974a69e03SAlistair Francis                     qemu_log_mask(LOG_GUEST_ERROR,
45074a69e03SAlistair Francis                                   "Guest bug: features[%i]=%x (expected 0)",
451a5cf2bb4SCornelia Huck                                   features.index, features.features);
452a5cf2bb4SCornelia Huck                     /* XXX: do a unit check here? */
453a5cf2bb4SCornelia Huck                 }
454a5cf2bb4SCornelia Huck             }
455a5cf2bb4SCornelia Huck             sch->curr_status.scsw.count = ccw.count - sizeof(features);
456a5cf2bb4SCornelia Huck             ret = 0;
457a5cf2bb4SCornelia Huck         }
458a5cf2bb4SCornelia Huck         break;
459a5cf2bb4SCornelia Huck     case CCW_CMD_READ_CONF:
460a5cf2bb4SCornelia Huck         if (check_len) {
461f24a6840SPaolo Bonzini             if (ccw.count > vdev->config_len) {
462a5cf2bb4SCornelia Huck                 ret = -EINVAL;
463a5cf2bb4SCornelia Huck                 break;
464a5cf2bb4SCornelia Huck             }
465a5cf2bb4SCornelia Huck         }
466f24a6840SPaolo Bonzini         len = MIN(ccw.count, vdev->config_len);
467a5cf2bb4SCornelia Huck         if (!ccw.cda) {
468a5cf2bb4SCornelia Huck             ret = -EFAULT;
469a5cf2bb4SCornelia Huck         } else {
470f24a6840SPaolo Bonzini             virtio_bus_get_vdev_config(&dev->bus, vdev->config);
471d895d25aSPierre Morel             ret = ccw_dstream_write_buf(&sch->cds, vdev->config, len);
472d895d25aSPierre Morel             if (ret) {
473a5cf2bb4SCornelia Huck                 sch->curr_status.scsw.count = ccw.count - len;
474d895d25aSPierre Morel             }
475a5cf2bb4SCornelia Huck         }
476a5cf2bb4SCornelia Huck         break;
477a5cf2bb4SCornelia Huck     case CCW_CMD_WRITE_CONF:
478a5cf2bb4SCornelia Huck         if (check_len) {
479f24a6840SPaolo Bonzini             if (ccw.count > vdev->config_len) {
480a5cf2bb4SCornelia Huck                 ret = -EINVAL;
481a5cf2bb4SCornelia Huck                 break;
482a5cf2bb4SCornelia Huck             }
483a5cf2bb4SCornelia Huck         }
484f24a6840SPaolo Bonzini         len = MIN(ccw.count, vdev->config_len);
485a5cf2bb4SCornelia Huck         if (!ccw.cda) {
486a5cf2bb4SCornelia Huck             ret = -EFAULT;
487a5cf2bb4SCornelia Huck         } else {
488f57ba058SHalil Pasic             ret = ccw_dstream_read_buf(&sch->cds, vdev->config, len);
489f57ba058SHalil Pasic             if (!ret) {
490f24a6840SPaolo Bonzini                 virtio_bus_set_vdev_config(&dev->bus, vdev->config);
491a5cf2bb4SCornelia Huck                 sch->curr_status.scsw.count = ccw.count - len;
492a5cf2bb4SCornelia Huck             }
493a5cf2bb4SCornelia Huck         }
494a5cf2bb4SCornelia Huck         break;
495e32652f7SPierre Morel     case CCW_CMD_READ_STATUS:
496e32652f7SPierre Morel         if (check_len) {
497e32652f7SPierre Morel             if (ccw.count != sizeof(status)) {
498e32652f7SPierre Morel                 ret = -EINVAL;
499e32652f7SPierre Morel                 break;
500e32652f7SPierre Morel             }
501e32652f7SPierre Morel         } else if (ccw.count < sizeof(status)) {
502e32652f7SPierre Morel             /* Can't execute command. */
503e32652f7SPierre Morel             ret = -EINVAL;
504e32652f7SPierre Morel             break;
505e32652f7SPierre Morel         }
506e32652f7SPierre Morel         if (!ccw.cda) {
507e32652f7SPierre Morel             ret = -EFAULT;
508e32652f7SPierre Morel         } else {
509e32652f7SPierre Morel             address_space_stb(&address_space_memory, ccw.cda, vdev->status,
510e32652f7SPierre Morel                                         MEMTXATTRS_UNSPECIFIED, NULL);
5113c254ab8SLadi Prosek             sch->curr_status.scsw.count = ccw.count - sizeof(vdev->status);
512e32652f7SPierre Morel             ret = 0;
513e32652f7SPierre Morel         }
514e32652f7SPierre Morel         break;
515a5cf2bb4SCornelia Huck     case CCW_CMD_WRITE_STATUS:
516a5cf2bb4SCornelia Huck         if (check_len) {
517a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(status)) {
518a5cf2bb4SCornelia Huck                 ret = -EINVAL;
519a5cf2bb4SCornelia Huck                 break;
520a5cf2bb4SCornelia Huck             }
521a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(status)) {
522a5cf2bb4SCornelia Huck             /* Can't execute command. */
523a5cf2bb4SCornelia Huck             ret = -EINVAL;
524a5cf2bb4SCornelia Huck             break;
525a5cf2bb4SCornelia Huck         }
526a5cf2bb4SCornelia Huck         if (!ccw.cda) {
527a5cf2bb4SCornelia Huck             ret = -EFAULT;
528a5cf2bb4SCornelia Huck         } else {
529d895d25aSPierre Morel             ret = ccw_dstream_read(&sch->cds, status);
530d895d25aSPierre Morel             if (ret) {
531d895d25aSPierre Morel                 break;
532d895d25aSPierre Morel             }
533b4436a0bSCornelia Huck             if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
534b4436a0bSCornelia Huck                 virtio_ccw_stop_ioeventfd(dev);
535b4436a0bSCornelia Huck             }
5360b352fd6SCornelia Huck             if (virtio_set_status(vdev, status) == 0) {
537f24a6840SPaolo Bonzini                 if (vdev->status == 0) {
538997340f3SPaolo Bonzini                     virtio_ccw_reset_virtio(dev);
539a5cf2bb4SCornelia Huck                 }
540b4436a0bSCornelia Huck                 if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
541b4436a0bSCornelia Huck                     virtio_ccw_start_ioeventfd(dev);
542b4436a0bSCornelia Huck                 }
543a5cf2bb4SCornelia Huck                 sch->curr_status.scsw.count = ccw.count - sizeof(status);
544a5cf2bb4SCornelia Huck                 ret = 0;
5450b352fd6SCornelia Huck             } else {
5460b352fd6SCornelia Huck                 /* Trigger a command reject. */
5470b352fd6SCornelia Huck                 ret = -ENOSYS;
5480b352fd6SCornelia Huck             }
549a5cf2bb4SCornelia Huck         }
550a5cf2bb4SCornelia Huck         break;
551a5cf2bb4SCornelia Huck     case CCW_CMD_SET_IND:
552a5cf2bb4SCornelia Huck         if (check_len) {
553a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(indicators)) {
554a5cf2bb4SCornelia Huck                 ret = -EINVAL;
555a5cf2bb4SCornelia Huck                 break;
556a5cf2bb4SCornelia Huck             }
557a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(indicators)) {
558a5cf2bb4SCornelia Huck             /* Can't execute command. */
559a5cf2bb4SCornelia Huck             ret = -EINVAL;
560a5cf2bb4SCornelia Huck             break;
561a5cf2bb4SCornelia Huck         }
5627e749462SCornelia Huck         if (sch->thinint_active) {
5637e749462SCornelia Huck             /* Trigger a command reject. */
5647e749462SCornelia Huck             ret = -ENOSYS;
5657e749462SCornelia Huck             break;
5667e749462SCornelia Huck         }
567797b6086SHalil Pasic         if (virtio_get_num_queues(vdev) > NR_CLASSIC_INDICATOR_BITS) {
568797b6086SHalil Pasic             /* More queues than indicator bits --> trigger a reject */
569797b6086SHalil Pasic             ret = -ENOSYS;
570797b6086SHalil Pasic             break;
571797b6086SHalil Pasic         }
572d1db1fa8SCornelia Huck         if (!ccw.cda) {
573a5cf2bb4SCornelia Huck             ret = -EFAULT;
574a5cf2bb4SCornelia Huck         } else {
575d895d25aSPierre Morel             ret = ccw_dstream_read(&sch->cds, indicators);
576d895d25aSPierre Morel             if (ret) {
577d895d25aSPierre Morel                 break;
578d895d25aSPierre Morel             }
579c9aacaadSPeter Maydell             indicators = be64_to_cpu(indicators);
5807bca3892SCornelia Huck             dev->indicators = get_indicator(indicators, sizeof(uint64_t));
581a5cf2bb4SCornelia Huck             sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
582a5cf2bb4SCornelia Huck             ret = 0;
583a5cf2bb4SCornelia Huck         }
584a5cf2bb4SCornelia Huck         break;
585a5cf2bb4SCornelia Huck     case CCW_CMD_SET_CONF_IND:
586a5cf2bb4SCornelia Huck         if (check_len) {
587a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(indicators)) {
588a5cf2bb4SCornelia Huck                 ret = -EINVAL;
589a5cf2bb4SCornelia Huck                 break;
590a5cf2bb4SCornelia Huck             }
591a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(indicators)) {
592a5cf2bb4SCornelia Huck             /* Can't execute command. */
593a5cf2bb4SCornelia Huck             ret = -EINVAL;
594a5cf2bb4SCornelia Huck             break;
595a5cf2bb4SCornelia Huck         }
596d1db1fa8SCornelia Huck         if (!ccw.cda) {
597a5cf2bb4SCornelia Huck             ret = -EFAULT;
598a5cf2bb4SCornelia Huck         } else {
599d895d25aSPierre Morel             ret = ccw_dstream_read(&sch->cds, indicators);
600d895d25aSPierre Morel             if (ret) {
601d895d25aSPierre Morel                 break;
602d895d25aSPierre Morel             }
603c9aacaadSPeter Maydell             indicators = be64_to_cpu(indicators);
6047bca3892SCornelia Huck             dev->indicators2 = get_indicator(indicators, sizeof(uint64_t));
605a5cf2bb4SCornelia Huck             sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
606a5cf2bb4SCornelia Huck             ret = 0;
607a5cf2bb4SCornelia Huck         }
608a5cf2bb4SCornelia Huck         break;
609a5cf2bb4SCornelia Huck     case CCW_CMD_READ_VQ_CONF:
610a5cf2bb4SCornelia Huck         if (check_len) {
611a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(vq_config)) {
612a5cf2bb4SCornelia Huck                 ret = -EINVAL;
613a5cf2bb4SCornelia Huck                 break;
614a5cf2bb4SCornelia Huck             }
615a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(vq_config)) {
616a5cf2bb4SCornelia Huck             /* Can't execute command. */
617a5cf2bb4SCornelia Huck             ret = -EINVAL;
618a5cf2bb4SCornelia Huck             break;
619a5cf2bb4SCornelia Huck         }
620a5cf2bb4SCornelia Huck         if (!ccw.cda) {
621a5cf2bb4SCornelia Huck             ret = -EFAULT;
622a5cf2bb4SCornelia Huck         } else {
623d895d25aSPierre Morel             ret = ccw_dstream_read(&sch->cds, vq_config.index);
624d895d25aSPierre Morel             if (ret) {
625d895d25aSPierre Morel                 break;
626d895d25aSPierre Morel             }
627c9aacaadSPeter Maydell             vq_config.index = be16_to_cpu(vq_config.index);
628b1914b82SHalil Pasic             if (vq_config.index >= VIRTIO_QUEUE_MAX) {
629d03a3630SCornelia Huck                 ret = -EINVAL;
630d03a3630SCornelia Huck                 break;
631d03a3630SCornelia Huck             }
632f24a6840SPaolo Bonzini             vq_config.num_max = virtio_queue_get_num(vdev,
633a5cf2bb4SCornelia Huck                                                      vq_config.index);
634c9aacaadSPeter Maydell             vq_config.num_max = cpu_to_be16(vq_config.num_max);
635d895d25aSPierre Morel             ret = ccw_dstream_write(&sch->cds, vq_config.num_max);
636d895d25aSPierre Morel             if (!ret) {
637a5cf2bb4SCornelia Huck                 sch->curr_status.scsw.count = ccw.count - sizeof(vq_config);
638d895d25aSPierre Morel             }
639a5cf2bb4SCornelia Huck         }
640a5cf2bb4SCornelia Huck         break;
6417e749462SCornelia Huck     case CCW_CMD_SET_IND_ADAPTER:
6427e749462SCornelia Huck         if (check_len) {
643f57ba058SHalil Pasic             if (ccw.count != sizeof(thinint)) {
6447e749462SCornelia Huck                 ret = -EINVAL;
6457e749462SCornelia Huck                 break;
6467e749462SCornelia Huck             }
647f57ba058SHalil Pasic         } else if (ccw.count < sizeof(thinint)) {
6487e749462SCornelia Huck             /* Can't execute command. */
6497e749462SCornelia Huck             ret = -EINVAL;
6507e749462SCornelia Huck             break;
6517e749462SCornelia Huck         }
6527e749462SCornelia Huck         if (!ccw.cda) {
6537e749462SCornelia Huck             ret = -EFAULT;
6547e749462SCornelia Huck         } else if (dev->indicators && !sch->thinint_active) {
6557e749462SCornelia Huck             /* Trigger a command reject. */
6567e749462SCornelia Huck             ret = -ENOSYS;
6577e749462SCornelia Huck         } else {
658f57ba058SHalil Pasic             if (ccw_dstream_read(&sch->cds, thinint)) {
6597e749462SCornelia Huck                 ret = -EFAULT;
6607e749462SCornelia Huck             } else {
661c9aacaadSPeter Maydell                 thinint.ind_bit = be64_to_cpu(thinint.ind_bit);
662c9aacaadSPeter Maydell                 thinint.summary_indicator =
663c9aacaadSPeter Maydell                     be64_to_cpu(thinint.summary_indicator);
664c9aacaadSPeter Maydell                 thinint.device_indicator =
665c9aacaadSPeter Maydell                     be64_to_cpu(thinint.device_indicator);
6667d45285fSCornelia Huck 
6677bca3892SCornelia Huck                 dev->summary_indicator =
668f57ba058SHalil Pasic                     get_indicator(thinint.summary_indicator, sizeof(uint8_t));
6697d45285fSCornelia Huck                 dev->indicators =
670f57ba058SHalil Pasic                     get_indicator(thinint.device_indicator,
671f57ba058SHalil Pasic                                   thinint.ind_bit / 8 + 1);
672f57ba058SHalil Pasic                 dev->thinint_isc = thinint.isc;
673f57ba058SHalil Pasic                 dev->routes.adapter.ind_offset = thinint.ind_bit;
674d426d9fbSCornelia Huck                 dev->routes.adapter.summary_offset = 7;
675dde522bbSFei Li                 dev->routes.adapter.adapter_id = css_get_adapter_id(
676dde522bbSFei Li                                                  CSS_IO_ADAPTER_VIRTIO,
677dde522bbSFei Li                                                  dev->thinint_isc);
6787bca3892SCornelia Huck                 sch->thinint_active = ((dev->indicators != NULL) &&
6797bca3892SCornelia Huck                                        (dev->summary_indicator != NULL));
680f57ba058SHalil Pasic                 sch->curr_status.scsw.count = ccw.count - sizeof(thinint);
6817e749462SCornelia Huck                 ret = 0;
6827e749462SCornelia Huck             }
6837e749462SCornelia Huck         }
6847e749462SCornelia Huck         break;
685c42767f2SThomas Huth     case CCW_CMD_SET_VIRTIO_REV:
686c42767f2SThomas Huth         len = sizeof(revinfo);
687c42767f2SThomas Huth         if (ccw.count < len) {
688c42767f2SThomas Huth             ret = -EINVAL;
689c42767f2SThomas Huth             break;
690c42767f2SThomas Huth         }
691c42767f2SThomas Huth         if (!ccw.cda) {
692c42767f2SThomas Huth             ret = -EFAULT;
693c42767f2SThomas Huth             break;
694c42767f2SThomas Huth         }
695d895d25aSPierre Morel         ret = ccw_dstream_read_buf(&sch->cds, &revinfo, 4);
696d895d25aSPierre Morel         if (ret < 0) {
697d895d25aSPierre Morel             break;
698d895d25aSPierre Morel         }
699c9aacaadSPeter Maydell         revinfo.revision = be16_to_cpu(revinfo.revision);
700c9aacaadSPeter Maydell         revinfo.length = be16_to_cpu(revinfo.length);
701c42767f2SThomas Huth         if (ccw.count < len + revinfo.length ||
702c42767f2SThomas Huth             (check_len && ccw.count > len + revinfo.length)) {
703c42767f2SThomas Huth             ret = -EINVAL;
704c42767f2SThomas Huth             break;
705c42767f2SThomas Huth         }
706c42767f2SThomas Huth         /*
707c42767f2SThomas Huth          * Once we start to support revisions with additional data, we'll
708c42767f2SThomas Huth          * need to fetch it here. Nothing to do for now, though.
709c42767f2SThomas Huth          */
710c42767f2SThomas Huth         if (dev->revision >= 0 ||
71147e13dfdSHalil Pasic             revinfo.revision > virtio_ccw_rev_max(dev) ||
71247e13dfdSHalil Pasic             (dev->force_revision_1 && !revinfo.revision)) {
713c42767f2SThomas Huth             ret = -ENOSYS;
714c42767f2SThomas Huth             break;
715c42767f2SThomas Huth         }
716c42767f2SThomas Huth         ret = 0;
717c42767f2SThomas Huth         dev->revision = revinfo.revision;
718c42767f2SThomas Huth         break;
719a5cf2bb4SCornelia Huck     default:
7208d034a6fSCornelia Huck         ret = -ENOSYS;
721a5cf2bb4SCornelia Huck         break;
722a5cf2bb4SCornelia Huck     }
723a5cf2bb4SCornelia Huck     return ret;
724a5cf2bb4SCornelia Huck }
725a5cf2bb4SCornelia Huck 
virtio_sch_disable_cb(SubchDev * sch)726c42767f2SThomas Huth static void virtio_sch_disable_cb(SubchDev *sch)
727c42767f2SThomas Huth {
728c42767f2SThomas Huth     VirtioCcwDevice *dev = sch->driver_data;
729c42767f2SThomas Huth 
730c42767f2SThomas Huth     dev->revision = -1;
731c42767f2SThomas Huth }
732c42767f2SThomas Huth 
virtio_ccw_device_realize(VirtioCcwDevice * dev,Error ** errp)7331fa75523SCornelia Huck static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp)
734a5cf2bb4SCornelia Huck {
7351fa75523SCornelia Huck     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
736b804e8a6SJing Liu     CcwDevice *ccw_dev = CCW_DEVICE(dev);
737d8d98db5SDong Jia Shi     CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev);
738817d4a6bSDong Jia Shi     SubchDev *sch;
739cf249935SSascha Silbe     Error *err = NULL;
7403c5fd807SCornelia Huck     int i;
741a5cf2bb4SCornelia Huck 
74236699ab4SCornelia Huck     sch = css_create_sch(ccw_dev->devno, errp);
743cf249935SSascha Silbe     if (!sch) {
744cf249935SSascha Silbe         return;
745cf249935SSascha Silbe     }
74647e13dfdSHalil Pasic     if (!virtio_ccw_rev_max(dev) && dev->force_revision_1) {
74747e13dfdSHalil Pasic         error_setg(&err, "Invalid value of property max_rev "
74847e13dfdSHalil Pasic                    "(is %d expected >= 1)", virtio_ccw_rev_max(dev));
749d8d98db5SDong Jia Shi         goto out_err;
75047e13dfdSHalil Pasic     }
751a5cf2bb4SCornelia Huck 
752a5cf2bb4SCornelia Huck     sch->driver_data = dev;
753a5cf2bb4SCornelia Huck     sch->ccw_cb = virtio_ccw_cb;
754c42767f2SThomas Huth     sch->disable_cb = virtio_sch_disable_cb;
755a5cf2bb4SCornelia Huck     sch->id.reserved = 0xff;
756a5cf2bb4SCornelia Huck     sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
757bab482d7SXiao Feng Ren     sch->do_subchannel_work = do_subchannel_work_virtual;
7580599a046SEric Farman     sch->irb_cb = build_irb_virtual;
759b804e8a6SJing Liu     ccw_dev->sch = sch;
760cf249935SSascha Silbe     dev->indicators = NULL;
761c42767f2SThomas Huth     dev->revision = -1;
7623c5fd807SCornelia Huck     for (i = 0; i < ADAPTER_ROUTES_MAX_GSI; i++) {
7633c5fd807SCornelia Huck         dev->routes.gsi[i] = -1;
7643c5fd807SCornelia Huck     }
765cf249935SSascha Silbe     css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE);
766cf249935SSascha Silbe 
767cf249935SSascha Silbe     trace_virtio_ccw_new_device(
768cf249935SSascha Silbe         sch->cssid, sch->ssid, sch->schid, sch->devno,
7692a78ac66SDong Jia Shi         ccw_dev->devno.valid ? "user-configured" : "auto-configured");
770c42767f2SThomas Huth 
7713909c079SPavel Dovgalyuk     /* fd-based ioevents can't be synchronized in record/replay */
7723909c079SPavel Dovgalyuk     if (replay_mode != REPLAY_MODE_NONE) {
7733909c079SPavel Dovgalyuk         dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD;
7743909c079SPavel Dovgalyuk     }
7753909c079SPavel Dovgalyuk 
7761fa75523SCornelia Huck     if (k->realize) {
7771fa75523SCornelia Huck         k->realize(dev, &err);
7781fa75523SCornelia Huck         if (err) {
779d8d98db5SDong Jia Shi             goto out_err;
780d8d98db5SDong Jia Shi         }
781d8d98db5SDong Jia Shi     }
782d8d98db5SDong Jia Shi 
783d8d98db5SDong Jia Shi     ck->realize(ccw_dev, &err);
784d8d98db5SDong Jia Shi     if (err) {
785d8d98db5SDong Jia Shi         goto out_err;
786d8d98db5SDong Jia Shi     }
787d8d98db5SDong Jia Shi 
788d8d98db5SDong Jia Shi     return;
789d8d98db5SDong Jia Shi 
790d8d98db5SDong Jia Shi out_err:
7911fa75523SCornelia Huck     error_propagate(errp, err);
79206e686eaSCornelia Huck     css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
793b804e8a6SJing Liu     ccw_dev->sch = NULL;
794a5cf2bb4SCornelia Huck     g_free(sch);
795a5cf2bb4SCornelia Huck }
796a5cf2bb4SCornelia Huck 
virtio_ccw_device_unrealize(VirtioCcwDevice * dev)797b69c3c21SMarkus Armbruster static void virtio_ccw_device_unrealize(VirtioCcwDevice *dev)
798a5cf2bb4SCornelia Huck {
7992d6ff33aSThomas Huth     VirtIOCCWDeviceClass *dc = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
800b804e8a6SJing Liu     CcwDevice *ccw_dev = CCW_DEVICE(dev);
801b804e8a6SJing Liu     SubchDev *sch = ccw_dev->sch;
802a5cf2bb4SCornelia Huck 
8032d6ff33aSThomas Huth     if (dc->unrealize) {
804b69c3c21SMarkus Armbruster         dc->unrealize(dev);
8052d6ff33aSThomas Huth     }
8062d6ff33aSThomas Huth 
807a5cf2bb4SCornelia Huck     if (sch) {
808a5cf2bb4SCornelia Huck         css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
809a5cf2bb4SCornelia Huck         g_free(sch);
81024118af8SNia Alarie         ccw_dev->sch = NULL;
811a5cf2bb4SCornelia Huck     }
8127bca3892SCornelia Huck     if (dev->indicators) {
813d426d9fbSCornelia Huck         release_indicator(&dev->routes.adapter, dev->indicators);
8147bca3892SCornelia Huck         dev->indicators = NULL;
8157bca3892SCornelia Huck     }
816a5cf2bb4SCornelia Huck }
817a5cf2bb4SCornelia Huck 
818a5cf2bb4SCornelia Huck /* DeviceState to VirtioCcwDevice. Note: used on datapath,
819a5cf2bb4SCornelia Huck  * be careful and test performance if you change this.
820a5cf2bb4SCornelia Huck  */
to_virtio_ccw_dev_fast(DeviceState * d)821a5cf2bb4SCornelia Huck static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d)
822a5cf2bb4SCornelia Huck {
823b804e8a6SJing Liu     CcwDevice *ccw_dev = to_ccw_dev_fast(d);
824b804e8a6SJing Liu 
825b804e8a6SJing Liu     return container_of(ccw_dev, VirtioCcwDevice, parent_obj);
826a5cf2bb4SCornelia Huck }
827a5cf2bb4SCornelia Huck 
virtio_set_ind_atomic(SubchDev * sch,uint64_t ind_loc,uint8_t to_be_set)8287e749462SCornelia Huck static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc,
8297e749462SCornelia Huck                                      uint8_t to_be_set)
8307e749462SCornelia Huck {
8311a8242f7SHalil Pasic     uint8_t expected, actual;
8327e749462SCornelia Huck     hwaddr len = 1;
8331a8242f7SHalil Pasic     /* avoid  multiple fetches */
8341a8242f7SHalil Pasic     uint8_t volatile *ind_addr;
8357e749462SCornelia Huck 
83685eb7c18SPhilippe Mathieu-Daudé     ind_addr = cpu_physical_memory_map(ind_loc, &len, true);
8377e749462SCornelia Huck     if (!ind_addr) {
8387e749462SCornelia Huck         error_report("%s(%x.%x.%04x): unable to access indicator",
8397e749462SCornelia Huck                      __func__, sch->cssid, sch->ssid, sch->schid);
8407e749462SCornelia Huck         return -1;
8417e749462SCornelia Huck     }
8421a8242f7SHalil Pasic     actual = *ind_addr;
8437e749462SCornelia Huck     do {
8441a8242f7SHalil Pasic         expected = actual;
845d73415a3SStefan Hajnoczi         actual = qatomic_cmpxchg(ind_addr, expected, expected | to_be_set);
8461a8242f7SHalil Pasic     } while (actual != expected);
8471a8242f7SHalil Pasic     trace_virtio_ccw_set_ind(ind_loc, actual, actual | to_be_set);
8481a8242f7SHalil Pasic     cpu_physical_memory_unmap((void *)ind_addr, len, 1, len);
8497e749462SCornelia Huck 
8501a8242f7SHalil Pasic     return actual;
8517e749462SCornelia Huck }
8527e749462SCornelia Huck 
virtio_ccw_notify(DeviceState * d,uint16_t vector)853a5cf2bb4SCornelia Huck static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
854a5cf2bb4SCornelia Huck {
855a5cf2bb4SCornelia Huck     VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d);
856b804e8a6SJing Liu     CcwDevice *ccw_dev = to_ccw_dev_fast(d);
857b804e8a6SJing Liu     SubchDev *sch = ccw_dev->sch;
858a5cf2bb4SCornelia Huck     uint64_t indicators;
859a5cf2bb4SCornelia Huck 
8607a5342e7SHalil Pasic     if (vector == VIRTIO_NO_VECTOR) {
861a5cf2bb4SCornelia Huck         return;
862a5cf2bb4SCornelia Huck     }
8637a5342e7SHalil Pasic     /*
8647a5342e7SHalil Pasic      * vector < VIRTIO_QUEUE_MAX: notification for a virtqueue
8657a5342e7SHalil Pasic      * vector == VIRTIO_QUEUE_MAX: configuration change notification
8667a5342e7SHalil Pasic      * bits beyond that are unused and should never be notified for
8677a5342e7SHalil Pasic      */
8687a5342e7SHalil Pasic     assert(vector <= VIRTIO_QUEUE_MAX);
869a5cf2bb4SCornelia Huck 
870b1914b82SHalil Pasic     if (vector < VIRTIO_QUEUE_MAX) {
8717c486976SCornelia Huck         if (!dev->indicators) {
8727c486976SCornelia Huck             return;
8737c486976SCornelia Huck         }
8747e749462SCornelia Huck         if (sch->thinint_active) {
8757e749462SCornelia Huck             /*
8767e749462SCornelia Huck              * In the adapter interrupt case, indicators points to a
8777e749462SCornelia Huck              * memory area that may be (way) larger than 64 bit and
8787e749462SCornelia Huck              * ind_bit indicates the start of the indicators in a big
8797e749462SCornelia Huck              * endian notation.
8807e749462SCornelia Huck              */
881d426d9fbSCornelia Huck             uint64_t ind_bit = dev->routes.adapter.ind_offset;
882d426d9fbSCornelia Huck 
8837bca3892SCornelia Huck             virtio_set_ind_atomic(sch, dev->indicators->addr +
884d426d9fbSCornelia Huck                                   (ind_bit + vector) / 8,
885d426d9fbSCornelia Huck                                   0x80 >> ((ind_bit + vector) % 8));
8867bca3892SCornelia Huck             if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr,
8877e749462SCornelia Huck                                        0x01)) {
88825a08b8dSYi Min Zhao                 css_adapter_interrupt(CSS_IO_ADAPTER_VIRTIO, dev->thinint_isc);
8897e749462SCornelia Huck             }
8907e749462SCornelia Huck         } else {
8917a5342e7SHalil Pasic             assert(vector < NR_CLASSIC_INDICATOR_BITS);
89242874d3aSPeter Maydell             indicators = address_space_ldq(&address_space_memory,
89342874d3aSPeter Maydell                                            dev->indicators->addr,
89442874d3aSPeter Maydell                                            MEMTXATTRS_UNSPECIFIED,
89542874d3aSPeter Maydell                                            NULL);
89619380b1bSCornelia Huck             indicators |= 1ULL << vector;
89742874d3aSPeter Maydell             address_space_stq(&address_space_memory, dev->indicators->addr,
89842874d3aSPeter Maydell                               indicators, MEMTXATTRS_UNSPECIFIED, NULL);
8997e749462SCornelia Huck             css_conditional_io_interrupt(sch);
9007e749462SCornelia Huck         }
901a5cf2bb4SCornelia Huck     } else {
9027c486976SCornelia Huck         if (!dev->indicators2) {
9037c486976SCornelia Huck             return;
9047c486976SCornelia Huck         }
90542874d3aSPeter Maydell         indicators = address_space_ldq(&address_space_memory,
90642874d3aSPeter Maydell                                        dev->indicators2->addr,
90742874d3aSPeter Maydell                                        MEMTXATTRS_UNSPECIFIED,
90842874d3aSPeter Maydell                                        NULL);
9097a5342e7SHalil Pasic         indicators |= 1ULL;
91042874d3aSPeter Maydell         address_space_stq(&address_space_memory, dev->indicators2->addr,
91142874d3aSPeter Maydell                           indicators, MEMTXATTRS_UNSPECIFIED, NULL);
912a5cf2bb4SCornelia Huck         css_conditional_io_interrupt(sch);
9137e749462SCornelia Huck     }
914a5cf2bb4SCornelia Huck }
915a5cf2bb4SCornelia Huck 
virtio_ccw_reset(DeviceState * d)916a5cf2bb4SCornelia Huck static void virtio_ccw_reset(DeviceState *d)
917a5cf2bb4SCornelia Huck {
918a5cf2bb4SCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
919838fb84fSCornelia Huck     VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
920a5cf2bb4SCornelia Huck 
921997340f3SPaolo Bonzini     virtio_ccw_reset_virtio(dev);
922838fb84fSCornelia Huck     if (vdc->parent_reset) {
923838fb84fSCornelia Huck         vdc->parent_reset(d);
924838fb84fSCornelia Huck     }
925a5cf2bb4SCornelia Huck }
926a5cf2bb4SCornelia Huck 
virtio_ccw_vmstate_change(DeviceState * d,bool running)927b4436a0bSCornelia Huck static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
928b4436a0bSCornelia Huck {
929b4436a0bSCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
930b4436a0bSCornelia Huck 
931b4436a0bSCornelia Huck     if (running) {
932b4436a0bSCornelia Huck         virtio_ccw_start_ioeventfd(dev);
933b4436a0bSCornelia Huck     } else {
934b4436a0bSCornelia Huck         virtio_ccw_stop_ioeventfd(dev);
935b4436a0bSCornelia Huck     }
936b4436a0bSCornelia Huck }
937b4436a0bSCornelia Huck 
virtio_ccw_query_guest_notifiers(DeviceState * d)938320ce850SCornelia Huck static bool virtio_ccw_query_guest_notifiers(DeviceState *d)
939320ce850SCornelia Huck {
940b804e8a6SJing Liu     CcwDevice *dev = CCW_DEVICE(d);
941320ce850SCornelia Huck 
942320ce850SCornelia Huck     return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA);
943320ce850SCornelia Huck }
944320ce850SCornelia Huck 
virtio_ccw_get_mappings(VirtioCcwDevice * dev)945d426d9fbSCornelia Huck static int virtio_ccw_get_mappings(VirtioCcwDevice *dev)
946d426d9fbSCornelia Huck {
947d426d9fbSCornelia Huck     int r;
948b804e8a6SJing Liu     CcwDevice *ccw_dev = CCW_DEVICE(dev);
949d426d9fbSCornelia Huck 
950b804e8a6SJing Liu     if (!ccw_dev->sch->thinint_active) {
951d426d9fbSCornelia Huck         return -EINVAL;
952d426d9fbSCornelia Huck     }
953d426d9fbSCornelia Huck 
954d426d9fbSCornelia Huck     r = map_indicator(&dev->routes.adapter, dev->summary_indicator);
955d426d9fbSCornelia Huck     if (r) {
956d426d9fbSCornelia Huck         return r;
957d426d9fbSCornelia Huck     }
958d426d9fbSCornelia Huck     r = map_indicator(&dev->routes.adapter, dev->indicators);
959d426d9fbSCornelia Huck     if (r) {
960d426d9fbSCornelia Huck         return r;
961d426d9fbSCornelia Huck     }
962d426d9fbSCornelia Huck     dev->routes.adapter.summary_addr = dev->summary_indicator->map;
963d426d9fbSCornelia Huck     dev->routes.adapter.ind_addr = dev->indicators->map;
964d426d9fbSCornelia Huck 
965d426d9fbSCornelia Huck     return 0;
966d426d9fbSCornelia Huck }
967d426d9fbSCornelia Huck 
virtio_ccw_setup_irqroutes(VirtioCcwDevice * dev,int nvqs)968d426d9fbSCornelia Huck static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs)
969d426d9fbSCornelia Huck {
970d426d9fbSCornelia Huck     int i;
971d426d9fbSCornelia Huck     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
972d426d9fbSCornelia Huck     int ret;
973d426d9fbSCornelia Huck     S390FLICState *fs = s390_get_flic();
9746762808fSDavid Hildenbrand     S390FLICStateClass *fsc = s390_get_flic_class(fs);
975d426d9fbSCornelia Huck 
976d426d9fbSCornelia Huck     ret = virtio_ccw_get_mappings(dev);
977d426d9fbSCornelia Huck     if (ret) {
978d426d9fbSCornelia Huck         return ret;
979d426d9fbSCornelia Huck     }
980d426d9fbSCornelia Huck     for (i = 0; i < nvqs; i++) {
981d426d9fbSCornelia Huck         if (!virtio_queue_get_num(vdev, i)) {
982d426d9fbSCornelia Huck             break;
983d426d9fbSCornelia Huck         }
984d426d9fbSCornelia Huck     }
985d426d9fbSCornelia Huck     dev->routes.num_routes = i;
986d426d9fbSCornelia Huck     return fsc->add_adapter_routes(fs, &dev->routes);
987d426d9fbSCornelia Huck }
988d426d9fbSCornelia Huck 
virtio_ccw_release_irqroutes(VirtioCcwDevice * dev,int nvqs)989d426d9fbSCornelia Huck static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs)
990d426d9fbSCornelia Huck {
991d426d9fbSCornelia Huck     S390FLICState *fs = s390_get_flic();
9926762808fSDavid Hildenbrand     S390FLICStateClass *fsc = s390_get_flic_class(fs);
993d426d9fbSCornelia Huck 
994d426d9fbSCornelia Huck     fsc->release_adapter_routes(fs, &dev->routes);
995d426d9fbSCornelia Huck }
996d426d9fbSCornelia Huck 
virtio_ccw_add_irqfd(VirtioCcwDevice * dev,int n)997d426d9fbSCornelia Huck static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n)
998d426d9fbSCornelia Huck {
999d426d9fbSCornelia Huck     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1000d426d9fbSCornelia Huck     VirtQueue *vq = virtio_get_queue(vdev, n);
1001d426d9fbSCornelia Huck     EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
1002d426d9fbSCornelia Huck 
10031c9b71a7SEric Auger     return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, notifier, NULL,
1004d426d9fbSCornelia Huck                                               dev->routes.gsi[n]);
1005d426d9fbSCornelia Huck }
1006d426d9fbSCornelia Huck 
virtio_ccw_remove_irqfd(VirtioCcwDevice * dev,int n)1007d426d9fbSCornelia Huck static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n)
1008d426d9fbSCornelia Huck {
1009d426d9fbSCornelia Huck     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1010d426d9fbSCornelia Huck     VirtQueue *vq = virtio_get_queue(vdev, n);
1011d426d9fbSCornelia Huck     EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
1012d426d9fbSCornelia Huck     int ret;
1013d426d9fbSCornelia Huck 
10141c9b71a7SEric Auger     ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, notifier,
1015d426d9fbSCornelia Huck                                                 dev->routes.gsi[n]);
1016d426d9fbSCornelia Huck     assert(ret == 0);
1017d426d9fbSCornelia Huck }
1018d426d9fbSCornelia Huck 
virtio_ccw_set_guest_notifier(VirtioCcwDevice * dev,int n,bool assign,bool with_irqfd)1019320ce850SCornelia Huck static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
1020320ce850SCornelia Huck                                          bool assign, bool with_irqfd)
1021320ce850SCornelia Huck {
1022f24a6840SPaolo Bonzini     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1023f24a6840SPaolo Bonzini     VirtQueue *vq = virtio_get_queue(vdev, n);
1024320ce850SCornelia Huck     EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
1025f24a6840SPaolo Bonzini     VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
1026320ce850SCornelia Huck 
1027320ce850SCornelia Huck     if (assign) {
1028320ce850SCornelia Huck         int r = event_notifier_init(notifier, 0);
1029320ce850SCornelia Huck 
1030320ce850SCornelia Huck         if (r < 0) {
1031320ce850SCornelia Huck             return r;
1032320ce850SCornelia Huck         }
1033320ce850SCornelia Huck         virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd);
1034d426d9fbSCornelia Huck         if (with_irqfd) {
1035d426d9fbSCornelia Huck             r = virtio_ccw_add_irqfd(dev, n);
1036d426d9fbSCornelia Huck             if (r) {
1037d426d9fbSCornelia Huck                 virtio_queue_set_guest_notifier_fd_handler(vq, false,
1038d426d9fbSCornelia Huck                                                            with_irqfd);
1039d426d9fbSCornelia Huck                 return r;
1040d426d9fbSCornelia Huck             }
1041d426d9fbSCornelia Huck         }
1042d426d9fbSCornelia Huck         /*
1043d426d9fbSCornelia Huck          * We do not support individual masking for channel devices, so we
1044d426d9fbSCornelia Huck          * need to manually trigger any guest masking callbacks here.
1045320ce850SCornelia Huck          */
10462858bc68SWei Huang         if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) {
1047f24a6840SPaolo Bonzini             k->guest_notifier_mask(vdev, n, false);
1048320ce850SCornelia Huck         }
1049320ce850SCornelia Huck         /* get lost events and re-inject */
1050320ce850SCornelia Huck         if (k->guest_notifier_pending &&
1051f24a6840SPaolo Bonzini             k->guest_notifier_pending(vdev, n)) {
1052320ce850SCornelia Huck             event_notifier_set(notifier);
1053320ce850SCornelia Huck         }
1054320ce850SCornelia Huck     } else {
10552858bc68SWei Huang         if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) {
1056f24a6840SPaolo Bonzini             k->guest_notifier_mask(vdev, n, true);
1057320ce850SCornelia Huck         }
1058d426d9fbSCornelia Huck         if (with_irqfd) {
1059d426d9fbSCornelia Huck             virtio_ccw_remove_irqfd(dev, n);
1060d426d9fbSCornelia Huck         }
1061320ce850SCornelia Huck         virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
1062320ce850SCornelia Huck         event_notifier_cleanup(notifier);
1063320ce850SCornelia Huck     }
1064320ce850SCornelia Huck     return 0;
1065320ce850SCornelia Huck }
1066320ce850SCornelia Huck 
virtio_ccw_set_guest_notifiers(DeviceState * d,int nvqs,bool assigned)1067320ce850SCornelia Huck static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
1068320ce850SCornelia Huck                                           bool assigned)
1069320ce850SCornelia Huck {
1070320ce850SCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1071f24a6840SPaolo Bonzini     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1072b804e8a6SJing Liu     CcwDevice *ccw_dev = CCW_DEVICE(d);
1073b804e8a6SJing Liu     bool with_irqfd = ccw_dev->sch->thinint_active && kvm_irqfds_enabled();
1074320ce850SCornelia Huck     int r, n;
1075320ce850SCornelia Huck 
1076d426d9fbSCornelia Huck     if (with_irqfd && assigned) {
1077d426d9fbSCornelia Huck         /* irq routes need to be set up before assigning irqfds */
1078d426d9fbSCornelia Huck         r = virtio_ccw_setup_irqroutes(dev, nvqs);
1079d426d9fbSCornelia Huck         if (r < 0) {
1080d426d9fbSCornelia Huck             goto irqroute_error;
1081d426d9fbSCornelia Huck         }
1082d426d9fbSCornelia Huck     }
1083320ce850SCornelia Huck     for (n = 0; n < nvqs; n++) {
1084320ce850SCornelia Huck         if (!virtio_queue_get_num(vdev, n)) {
1085320ce850SCornelia Huck             break;
1086320ce850SCornelia Huck         }
1087d426d9fbSCornelia Huck         r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd);
1088320ce850SCornelia Huck         if (r < 0) {
1089320ce850SCornelia Huck             goto assign_error;
1090320ce850SCornelia Huck         }
1091320ce850SCornelia Huck     }
1092d426d9fbSCornelia Huck     if (with_irqfd && !assigned) {
1093d426d9fbSCornelia Huck         /* release irq routes after irqfds have been released */
1094d426d9fbSCornelia Huck         virtio_ccw_release_irqroutes(dev, nvqs);
1095d426d9fbSCornelia Huck     }
1096320ce850SCornelia Huck     return 0;
1097320ce850SCornelia Huck 
1098320ce850SCornelia Huck assign_error:
1099320ce850SCornelia Huck     while (--n >= 0) {
1100320ce850SCornelia Huck         virtio_ccw_set_guest_notifier(dev, n, !assigned, false);
1101320ce850SCornelia Huck     }
1102d426d9fbSCornelia Huck irqroute_error:
1103d426d9fbSCornelia Huck     if (with_irqfd && assigned) {
1104d426d9fbSCornelia Huck         virtio_ccw_release_irqroutes(dev, nvqs);
1105d426d9fbSCornelia Huck     }
1106320ce850SCornelia Huck     return r;
1107320ce850SCornelia Huck }
1108320ce850SCornelia Huck 
virtio_ccw_save_queue(DeviceState * d,int n,QEMUFile * f)1109bcb2b582SJens Freimann static void virtio_ccw_save_queue(DeviceState *d, int n, QEMUFile *f)
1110bcb2b582SJens Freimann {
1111bcb2b582SJens Freimann     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1112bcb2b582SJens Freimann     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1113bcb2b582SJens Freimann 
1114bcb2b582SJens Freimann     qemu_put_be16(f, virtio_queue_vector(vdev, n));
1115bcb2b582SJens Freimann }
1116bcb2b582SJens Freimann 
virtio_ccw_load_queue(DeviceState * d,int n,QEMUFile * f)1117bcb2b582SJens Freimann static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f)
1118bcb2b582SJens Freimann {
1119bcb2b582SJens Freimann     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1120bcb2b582SJens Freimann     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1121bcb2b582SJens Freimann     uint16_t vector;
1122bcb2b582SJens Freimann 
1123bcb2b582SJens Freimann     qemu_get_be16s(f, &vector);
1124bcb2b582SJens Freimann     virtio_queue_set_vector(vdev, n , vector);
1125bcb2b582SJens Freimann 
1126bcb2b582SJens Freimann     return 0;
1127bcb2b582SJens Freimann }
1128bcb2b582SJens Freimann 
virtio_ccw_save_config(DeviceState * d,QEMUFile * f)1129bcb2b582SJens Freimann static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
1130bcb2b582SJens Freimann {
1131bcb2b582SJens Freimann     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1132517ff12cSHalil Pasic     vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL);
1133bcb2b582SJens Freimann }
1134bcb2b582SJens Freimann 
virtio_ccw_load_config(DeviceState * d,QEMUFile * f)1135bcb2b582SJens Freimann static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
1136bcb2b582SJens Freimann {
1137bcb2b582SJens Freimann     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1138517ff12cSHalil Pasic     return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1);
1139bcb2b582SJens Freimann }
1140bcb2b582SJens Freimann 
virtio_ccw_pre_plugged(DeviceState * d,Error ** errp)1141d1b4259fSMaxime Coquelin static void virtio_ccw_pre_plugged(DeviceState *d, Error **errp)
1142d1b4259fSMaxime Coquelin {
1143d1b4259fSMaxime Coquelin    VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1144d1b4259fSMaxime Coquelin    VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1145d1b4259fSMaxime Coquelin 
1146d1b4259fSMaxime Coquelin     if (dev->max_rev >= 1) {
1147d1b4259fSMaxime Coquelin         virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1);
1148d1b4259fSMaxime Coquelin     }
1149d1b4259fSMaxime Coquelin }
1150d1b4259fSMaxime Coquelin 
1151fb846a09SCornelia Huck /* This is called by virtio-bus just after the device is plugged. */
virtio_ccw_device_plugged(DeviceState * d,Error ** errp)1152e8398045SJason Wang static void virtio_ccw_device_plugged(DeviceState *d, Error **errp)
1153fb846a09SCornelia Huck {
1154fb846a09SCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
115510ceaa1eSJason Wang     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1156b804e8a6SJing Liu     CcwDevice *ccw_dev = CCW_DEVICE(d);
1157b804e8a6SJing Liu     SubchDev *sch = ccw_dev->sch;
115810ceaa1eSJason Wang     int n = virtio_get_num_queues(vdev);
11590708afa7SHalil Pasic     S390FLICState *flic = s390_get_flic();
116010ceaa1eSJason Wang 
1161d1b4259fSMaxime Coquelin     if (!virtio_has_feature(vdev->host_features, VIRTIO_F_VERSION_1)) {
1162d1b4259fSMaxime Coquelin         dev->max_rev = 0;
1163d1b4259fSMaxime Coquelin     }
1164d1b4259fSMaxime Coquelin 
11659b3a35ecSCornelia Huck     if (!virtio_ccw_rev_max(dev) && !virtio_legacy_allowed(vdev)) {
1166d55f5182SStefano Garzarella         /*
1167d55f5182SStefano Garzarella          * To avoid migration issues, we allow legacy mode when legacy
1168d55f5182SStefano Garzarella          * check is disabled in the old machine types (< 5.1).
1169d55f5182SStefano Garzarella          */
1170d55f5182SStefano Garzarella         if (virtio_legacy_check_disabled(vdev)) {
1171d55f5182SStefano Garzarella             warn_report("device requires revision >= 1, but for backward "
1172d55f5182SStefano Garzarella                         "compatibility max_revision=0 is allowed");
1173d55f5182SStefano Garzarella         } else {
11749b3a35ecSCornelia Huck             error_setg(errp, "Invalid value of property max_rev "
11759b3a35ecSCornelia Huck                        "(is %d expected >= 1)", virtio_ccw_rev_max(dev));
11769b3a35ecSCornelia Huck             return;
11779b3a35ecSCornelia Huck         }
1178d55f5182SStefano Garzarella     }
11799b3a35ecSCornelia Huck 
1180b1914b82SHalil Pasic     if (virtio_get_num_queues(vdev) > VIRTIO_QUEUE_MAX) {
1181b34aee54SMichael Tokarev         error_setg(errp, "The number of virtqueues %d "
1182b1914b82SHalil Pasic                    "exceeds virtio limit %d", n,
1183b1914b82SHalil Pasic                    VIRTIO_QUEUE_MAX);
118410ceaa1eSJason Wang         return;
118510ceaa1eSJason Wang     }
11860708afa7SHalil Pasic     if (virtio_get_num_queues(vdev) > flic->adapter_routes_max_batch) {
11870708afa7SHalil Pasic         error_setg(errp, "The number of virtqueues %d "
11880708afa7SHalil Pasic                    "exceeds flic adapter route limit %d", n,
11890708afa7SHalil Pasic                    flic->adapter_routes_max_batch);
11900708afa7SHalil Pasic         return;
11910708afa7SHalil Pasic     }
1192fb846a09SCornelia Huck 
1193fb846a09SCornelia Huck     sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus);
1194fb846a09SCornelia Huck 
1195542571d5SCornelia Huck 
1196fb846a09SCornelia Huck     css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid,
1197fb846a09SCornelia Huck                           d->hotplugged, 1);
1198fb846a09SCornelia Huck }
1199fb846a09SCornelia Huck 
virtio_ccw_device_unplugged(DeviceState * d)1200fb846a09SCornelia Huck static void virtio_ccw_device_unplugged(DeviceState *d)
1201fb846a09SCornelia Huck {
1202fb846a09SCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1203fb846a09SCornelia Huck 
1204fb846a09SCornelia Huck     virtio_ccw_stop_ioeventfd(dev);
1205fb846a09SCornelia Huck }
1206a5cf2bb4SCornelia Huck /**************** Virtio-ccw Bus Device Descriptions *******************/
1207a5cf2bb4SCornelia Huck 
virtio_ccw_busdev_realize(DeviceState * dev,Error ** errp)12085e5ced38SMarkus Armbruster static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp)
1209a5cf2bb4SCornelia Huck {
1210a5cf2bb4SCornelia Huck     VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
1211a5cf2bb4SCornelia Huck 
12121bf4d7aaSAndreas Färber     virtio_ccw_bus_new(&_dev->bus, sizeof(_dev->bus), _dev);
12131fa75523SCornelia Huck     virtio_ccw_device_realize(_dev, errp);
1214a5cf2bb4SCornelia Huck }
1215a5cf2bb4SCornelia Huck 
virtio_ccw_busdev_unrealize(DeviceState * dev)1216b69c3c21SMarkus Armbruster static void virtio_ccw_busdev_unrealize(DeviceState *dev)
1217a5cf2bb4SCornelia Huck {
1218a5cf2bb4SCornelia Huck     VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
1219a5cf2bb4SCornelia Huck 
1220b69c3c21SMarkus Armbruster     virtio_ccw_device_unrealize(_dev);
1221a5cf2bb4SCornelia Huck }
1222a5cf2bb4SCornelia Huck 
virtio_ccw_busdev_unplug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)1223b804e8a6SJing Liu static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev,
1224277bc95eSIgor Mammedov                                      DeviceState *dev, Error **errp)
1225a5cf2bb4SCornelia Huck {
1226b804e8a6SJing Liu     VirtioCcwDevice *_dev = to_virtio_ccw_dev_fast(dev);
1227a5cf2bb4SCornelia Huck 
12280b81c1efSPaolo Bonzini     virtio_ccw_stop_ioeventfd(_dev);
1229a5cf2bb4SCornelia Huck }
1230a5cf2bb4SCornelia Huck 
virtio_ccw_device_class_init(ObjectClass * klass,void * data)1231a5cf2bb4SCornelia Huck static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
1232a5cf2bb4SCornelia Huck {
1233a5cf2bb4SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(klass);
1234b804e8a6SJing Liu     CCWDeviceClass *k = CCW_DEVICE_CLASS(dc);
1235838fb84fSCornelia Huck     VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_CLASS(klass);
1236a5cf2bb4SCornelia Huck 
1237b804e8a6SJing Liu     k->unplug = virtio_ccw_busdev_unplug;
12385e5ced38SMarkus Armbruster     dc->realize = virtio_ccw_busdev_realize;
123924118af8SNia Alarie     dc->unrealize = virtio_ccw_busdev_unrealize;
1240838fb84fSCornelia Huck     device_class_set_parent_reset(dc, virtio_ccw_reset, &vdc->parent_reset);
1241a5cf2bb4SCornelia Huck }
1242a5cf2bb4SCornelia Huck 
1243a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_device_info = {
1244a5cf2bb4SCornelia Huck     .name = TYPE_VIRTIO_CCW_DEVICE,
1245b804e8a6SJing Liu     .parent = TYPE_CCW_DEVICE,
1246a5cf2bb4SCornelia Huck     .instance_size = sizeof(VirtioCcwDevice),
1247a5cf2bb4SCornelia Huck     .class_init = virtio_ccw_device_class_init,
1248a5cf2bb4SCornelia Huck     .class_size = sizeof(VirtIOCCWDeviceClass),
1249a5cf2bb4SCornelia Huck     .abstract = true,
1250a5cf2bb4SCornelia Huck };
1251a5cf2bb4SCornelia Huck 
1252a5cf2bb4SCornelia Huck /* virtio-ccw-bus */
1253a5cf2bb4SCornelia Huck 
virtio_ccw_bus_new(VirtioBusState * bus,size_t bus_size,VirtioCcwDevice * dev)12541bf4d7aaSAndreas Färber static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
12551bf4d7aaSAndreas Färber                                VirtioCcwDevice *dev)
1256a5cf2bb4SCornelia Huck {
1257a5cf2bb4SCornelia Huck     DeviceState *qdev = DEVICE(dev);
1258f4dd69aaSKONRAD Frederic     char virtio_bus_name[] = "virtio-bus";
1259a5cf2bb4SCornelia Huck 
1260d637e1dcSPeter Maydell     qbus_init(bus, bus_size, TYPE_VIRTIO_CCW_BUS, qdev, virtio_bus_name);
1261a5cf2bb4SCornelia Huck }
1262a5cf2bb4SCornelia Huck 
virtio_ccw_bus_class_init(ObjectClass * klass,void * data)1263a5cf2bb4SCornelia Huck static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
1264a5cf2bb4SCornelia Huck {
1265a5cf2bb4SCornelia Huck     VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
1266a5cf2bb4SCornelia Huck     BusClass *bus_class = BUS_CLASS(klass);
1267a5cf2bb4SCornelia Huck 
1268a5cf2bb4SCornelia Huck     bus_class->max_dev = 1;
1269a5cf2bb4SCornelia Huck     k->notify = virtio_ccw_notify;
1270b4436a0bSCornelia Huck     k->vmstate_change = virtio_ccw_vmstate_change;
1271320ce850SCornelia Huck     k->query_guest_notifiers = virtio_ccw_query_guest_notifiers;
1272320ce850SCornelia Huck     k->set_guest_notifiers = virtio_ccw_set_guest_notifiers;
1273bcb2b582SJens Freimann     k->save_queue = virtio_ccw_save_queue;
1274bcb2b582SJens Freimann     k->load_queue = virtio_ccw_load_queue;
1275bcb2b582SJens Freimann     k->save_config = virtio_ccw_save_config;
1276bcb2b582SJens Freimann     k->load_config = virtio_ccw_load_config;
1277d1b4259fSMaxime Coquelin     k->pre_plugged = virtio_ccw_pre_plugged;
1278fb846a09SCornelia Huck     k->device_plugged = virtio_ccw_device_plugged;
1279fb846a09SCornelia Huck     k->device_unplugged = virtio_ccw_device_unplugged;
12808e93cef1SPaolo Bonzini     k->ioeventfd_enabled = virtio_ccw_ioeventfd_enabled;
12817c55f68aSCornelia Huck     k->ioeventfd_assign = virtio_ccw_ioeventfd_assign;
1282a5cf2bb4SCornelia Huck }
1283a5cf2bb4SCornelia Huck 
1284a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_bus_info = {
1285a5cf2bb4SCornelia Huck     .name = TYPE_VIRTIO_CCW_BUS,
1286a5cf2bb4SCornelia Huck     .parent = TYPE_VIRTIO_BUS,
1287a5cf2bb4SCornelia Huck     .instance_size = sizeof(VirtioCcwBusState),
128874ded8b4SCornelia Huck     .class_size = sizeof(VirtioCcwBusClass),
1289a5cf2bb4SCornelia Huck     .class_init = virtio_ccw_bus_class_init,
1290a5cf2bb4SCornelia Huck };
1291a5cf2bb4SCornelia Huck 
virtio_ccw_register(void)1292a5cf2bb4SCornelia Huck static void virtio_ccw_register(void)
1293a5cf2bb4SCornelia Huck {
1294a5cf2bb4SCornelia Huck     type_register_static(&virtio_ccw_bus_info);
1295a5cf2bb4SCornelia Huck     type_register_static(&virtio_ccw_device_info);
1296a5cf2bb4SCornelia Huck }
1297a5cf2bb4SCornelia Huck 
1298a5cf2bb4SCornelia Huck type_init(virtio_ccw_register)
1299