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