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,
90b9b59a36SRichard 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,
101b9b59a36SRichard 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_hold(Object * obj,ResetType type)916*6affa00dSPeter Maydell static void virtio_ccw_reset_hold(Object *obj, ResetType type)
917a5cf2bb4SCornelia Huck {
918*6affa00dSPeter Maydell VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(obj);
919838fb84fSCornelia Huck VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
920a5cf2bb4SCornelia Huck
921997340f3SPaolo Bonzini virtio_ccw_reset_virtio(dev);
922*6affa00dSPeter Maydell
923*6affa00dSPeter Maydell if (vdc->parent_phases.hold) {
924*6affa00dSPeter Maydell vdc->parent_phases.hold(obj, type);
925838fb84fSCornelia Huck }
926a5cf2bb4SCornelia Huck }
927a5cf2bb4SCornelia Huck
virtio_ccw_vmstate_change(DeviceState * d,bool running)928b4436a0bSCornelia Huck static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
929b4436a0bSCornelia Huck {
930b4436a0bSCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
931b4436a0bSCornelia Huck
932b4436a0bSCornelia Huck if (running) {
933b4436a0bSCornelia Huck virtio_ccw_start_ioeventfd(dev);
934b4436a0bSCornelia Huck } else {
935b4436a0bSCornelia Huck virtio_ccw_stop_ioeventfd(dev);
936b4436a0bSCornelia Huck }
937b4436a0bSCornelia Huck }
938b4436a0bSCornelia Huck
virtio_ccw_query_guest_notifiers(DeviceState * d)939320ce850SCornelia Huck static bool virtio_ccw_query_guest_notifiers(DeviceState *d)
940320ce850SCornelia Huck {
941b804e8a6SJing Liu CcwDevice *dev = CCW_DEVICE(d);
942320ce850SCornelia Huck
943320ce850SCornelia Huck return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA);
944320ce850SCornelia Huck }
945320ce850SCornelia Huck
virtio_ccw_get_mappings(VirtioCcwDevice * dev)946d426d9fbSCornelia Huck static int virtio_ccw_get_mappings(VirtioCcwDevice *dev)
947d426d9fbSCornelia Huck {
948d426d9fbSCornelia Huck int r;
949b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(dev);
950d426d9fbSCornelia Huck
951b804e8a6SJing Liu if (!ccw_dev->sch->thinint_active) {
952d426d9fbSCornelia Huck return -EINVAL;
953d426d9fbSCornelia Huck }
954d426d9fbSCornelia Huck
955d426d9fbSCornelia Huck r = map_indicator(&dev->routes.adapter, dev->summary_indicator);
956d426d9fbSCornelia Huck if (r) {
957d426d9fbSCornelia Huck return r;
958d426d9fbSCornelia Huck }
959d426d9fbSCornelia Huck r = map_indicator(&dev->routes.adapter, dev->indicators);
960d426d9fbSCornelia Huck if (r) {
961d426d9fbSCornelia Huck return r;
962d426d9fbSCornelia Huck }
963d426d9fbSCornelia Huck dev->routes.adapter.summary_addr = dev->summary_indicator->map;
964d426d9fbSCornelia Huck dev->routes.adapter.ind_addr = dev->indicators->map;
965d426d9fbSCornelia Huck
966d426d9fbSCornelia Huck return 0;
967d426d9fbSCornelia Huck }
968d426d9fbSCornelia Huck
virtio_ccw_setup_irqroutes(VirtioCcwDevice * dev,int nvqs)969d426d9fbSCornelia Huck static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs)
970d426d9fbSCornelia Huck {
971d426d9fbSCornelia Huck int i;
972d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
973d426d9fbSCornelia Huck int ret;
974d426d9fbSCornelia Huck S390FLICState *fs = s390_get_flic();
9756762808fSDavid Hildenbrand S390FLICStateClass *fsc = s390_get_flic_class(fs);
976d426d9fbSCornelia Huck
977d426d9fbSCornelia Huck ret = virtio_ccw_get_mappings(dev);
978d426d9fbSCornelia Huck if (ret) {
979d426d9fbSCornelia Huck return ret;
980d426d9fbSCornelia Huck }
981d426d9fbSCornelia Huck for (i = 0; i < nvqs; i++) {
982d426d9fbSCornelia Huck if (!virtio_queue_get_num(vdev, i)) {
983d426d9fbSCornelia Huck break;
984d426d9fbSCornelia Huck }
985d426d9fbSCornelia Huck }
986d426d9fbSCornelia Huck dev->routes.num_routes = i;
987d426d9fbSCornelia Huck return fsc->add_adapter_routes(fs, &dev->routes);
988d426d9fbSCornelia Huck }
989d426d9fbSCornelia Huck
virtio_ccw_release_irqroutes(VirtioCcwDevice * dev,int nvqs)990d426d9fbSCornelia Huck static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs)
991d426d9fbSCornelia Huck {
992d426d9fbSCornelia Huck S390FLICState *fs = s390_get_flic();
9936762808fSDavid Hildenbrand S390FLICStateClass *fsc = s390_get_flic_class(fs);
994d426d9fbSCornelia Huck
995d426d9fbSCornelia Huck fsc->release_adapter_routes(fs, &dev->routes);
996d426d9fbSCornelia Huck }
997d426d9fbSCornelia Huck
virtio_ccw_add_irqfd(VirtioCcwDevice * dev,int n)998d426d9fbSCornelia Huck static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n)
999d426d9fbSCornelia Huck {
1000d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1001d426d9fbSCornelia Huck VirtQueue *vq = virtio_get_queue(vdev, n);
1002d426d9fbSCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
1003d426d9fbSCornelia Huck
10041c9b71a7SEric Auger return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, notifier, NULL,
1005d426d9fbSCornelia Huck dev->routes.gsi[n]);
1006d426d9fbSCornelia Huck }
1007d426d9fbSCornelia Huck
virtio_ccw_remove_irqfd(VirtioCcwDevice * dev,int n)1008d426d9fbSCornelia Huck static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n)
1009d426d9fbSCornelia Huck {
1010d426d9fbSCornelia Huck VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1011d426d9fbSCornelia Huck VirtQueue *vq = virtio_get_queue(vdev, n);
1012d426d9fbSCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
1013d426d9fbSCornelia Huck int ret;
1014d426d9fbSCornelia Huck
10151c9b71a7SEric Auger ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, notifier,
1016d426d9fbSCornelia Huck dev->routes.gsi[n]);
1017d426d9fbSCornelia Huck assert(ret == 0);
1018d426d9fbSCornelia Huck }
1019d426d9fbSCornelia Huck
virtio_ccw_set_guest_notifier(VirtioCcwDevice * dev,int n,bool assign,bool with_irqfd)1020320ce850SCornelia Huck static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
1021320ce850SCornelia Huck bool assign, bool with_irqfd)
1022320ce850SCornelia Huck {
1023f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1024f24a6840SPaolo Bonzini VirtQueue *vq = virtio_get_queue(vdev, n);
1025320ce850SCornelia Huck EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
1026f24a6840SPaolo Bonzini VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
1027320ce850SCornelia Huck
1028320ce850SCornelia Huck if (assign) {
1029320ce850SCornelia Huck int r = event_notifier_init(notifier, 0);
1030320ce850SCornelia Huck
1031320ce850SCornelia Huck if (r < 0) {
1032320ce850SCornelia Huck return r;
1033320ce850SCornelia Huck }
1034320ce850SCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd);
1035d426d9fbSCornelia Huck if (with_irqfd) {
1036d426d9fbSCornelia Huck r = virtio_ccw_add_irqfd(dev, n);
1037d426d9fbSCornelia Huck if (r) {
1038d426d9fbSCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, false,
1039d426d9fbSCornelia Huck with_irqfd);
1040d426d9fbSCornelia Huck return r;
1041d426d9fbSCornelia Huck }
1042d426d9fbSCornelia Huck }
1043d426d9fbSCornelia Huck /*
1044d426d9fbSCornelia Huck * We do not support individual masking for channel devices, so we
1045d426d9fbSCornelia Huck * need to manually trigger any guest masking callbacks here.
1046320ce850SCornelia Huck */
10472858bc68SWei Huang if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) {
1048f24a6840SPaolo Bonzini k->guest_notifier_mask(vdev, n, false);
1049320ce850SCornelia Huck }
1050320ce850SCornelia Huck /* get lost events and re-inject */
1051320ce850SCornelia Huck if (k->guest_notifier_pending &&
1052f24a6840SPaolo Bonzini k->guest_notifier_pending(vdev, n)) {
1053320ce850SCornelia Huck event_notifier_set(notifier);
1054320ce850SCornelia Huck }
1055320ce850SCornelia Huck } else {
10562858bc68SWei Huang if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) {
1057f24a6840SPaolo Bonzini k->guest_notifier_mask(vdev, n, true);
1058320ce850SCornelia Huck }
1059d426d9fbSCornelia Huck if (with_irqfd) {
1060d426d9fbSCornelia Huck virtio_ccw_remove_irqfd(dev, n);
1061d426d9fbSCornelia Huck }
1062320ce850SCornelia Huck virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
1063320ce850SCornelia Huck event_notifier_cleanup(notifier);
1064320ce850SCornelia Huck }
1065320ce850SCornelia Huck return 0;
1066320ce850SCornelia Huck }
1067320ce850SCornelia Huck
virtio_ccw_set_guest_notifiers(DeviceState * d,int nvqs,bool assigned)1068320ce850SCornelia Huck static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
1069320ce850SCornelia Huck bool assigned)
1070320ce850SCornelia Huck {
1071320ce850SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1072f24a6840SPaolo Bonzini VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1073b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(d);
1074b804e8a6SJing Liu bool with_irqfd = ccw_dev->sch->thinint_active && kvm_irqfds_enabled();
1075320ce850SCornelia Huck int r, n;
1076320ce850SCornelia Huck
1077d426d9fbSCornelia Huck if (with_irqfd && assigned) {
1078d426d9fbSCornelia Huck /* irq routes need to be set up before assigning irqfds */
1079d426d9fbSCornelia Huck r = virtio_ccw_setup_irqroutes(dev, nvqs);
1080d426d9fbSCornelia Huck if (r < 0) {
1081d426d9fbSCornelia Huck goto irqroute_error;
1082d426d9fbSCornelia Huck }
1083d426d9fbSCornelia Huck }
1084320ce850SCornelia Huck for (n = 0; n < nvqs; n++) {
1085320ce850SCornelia Huck if (!virtio_queue_get_num(vdev, n)) {
1086320ce850SCornelia Huck break;
1087320ce850SCornelia Huck }
1088d426d9fbSCornelia Huck r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd);
1089320ce850SCornelia Huck if (r < 0) {
1090320ce850SCornelia Huck goto assign_error;
1091320ce850SCornelia Huck }
1092320ce850SCornelia Huck }
1093d426d9fbSCornelia Huck if (with_irqfd && !assigned) {
1094d426d9fbSCornelia Huck /* release irq routes after irqfds have been released */
1095d426d9fbSCornelia Huck virtio_ccw_release_irqroutes(dev, nvqs);
1096d426d9fbSCornelia Huck }
1097320ce850SCornelia Huck return 0;
1098320ce850SCornelia Huck
1099320ce850SCornelia Huck assign_error:
1100320ce850SCornelia Huck while (--n >= 0) {
1101320ce850SCornelia Huck virtio_ccw_set_guest_notifier(dev, n, !assigned, false);
1102320ce850SCornelia Huck }
1103d426d9fbSCornelia Huck irqroute_error:
1104d426d9fbSCornelia Huck if (with_irqfd && assigned) {
1105d426d9fbSCornelia Huck virtio_ccw_release_irqroutes(dev, nvqs);
1106d426d9fbSCornelia Huck }
1107320ce850SCornelia Huck return r;
1108320ce850SCornelia Huck }
1109320ce850SCornelia Huck
virtio_ccw_save_queue(DeviceState * d,int n,QEMUFile * f)1110bcb2b582SJens Freimann static void virtio_ccw_save_queue(DeviceState *d, int n, QEMUFile *f)
1111bcb2b582SJens Freimann {
1112bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1113bcb2b582SJens Freimann VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1114bcb2b582SJens Freimann
1115bcb2b582SJens Freimann qemu_put_be16(f, virtio_queue_vector(vdev, n));
1116bcb2b582SJens Freimann }
1117bcb2b582SJens Freimann
virtio_ccw_load_queue(DeviceState * d,int n,QEMUFile * f)1118bcb2b582SJens Freimann static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f)
1119bcb2b582SJens Freimann {
1120bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1121bcb2b582SJens Freimann VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1122bcb2b582SJens Freimann uint16_t vector;
1123bcb2b582SJens Freimann
1124bcb2b582SJens Freimann qemu_get_be16s(f, &vector);
1125bcb2b582SJens Freimann virtio_queue_set_vector(vdev, n , vector);
1126bcb2b582SJens Freimann
1127bcb2b582SJens Freimann return 0;
1128bcb2b582SJens Freimann }
1129bcb2b582SJens Freimann
virtio_ccw_save_config(DeviceState * d,QEMUFile * f)1130bcb2b582SJens Freimann static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
1131bcb2b582SJens Freimann {
1132bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1133517ff12cSHalil Pasic vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL);
1134bcb2b582SJens Freimann }
1135bcb2b582SJens Freimann
virtio_ccw_load_config(DeviceState * d,QEMUFile * f)1136bcb2b582SJens Freimann static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
1137bcb2b582SJens Freimann {
1138bcb2b582SJens Freimann VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1139517ff12cSHalil Pasic return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1);
1140bcb2b582SJens Freimann }
1141bcb2b582SJens Freimann
virtio_ccw_pre_plugged(DeviceState * d,Error ** errp)1142d1b4259fSMaxime Coquelin static void virtio_ccw_pre_plugged(DeviceState *d, Error **errp)
1143d1b4259fSMaxime Coquelin {
1144d1b4259fSMaxime Coquelin VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1145d1b4259fSMaxime Coquelin VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1146d1b4259fSMaxime Coquelin
1147d1b4259fSMaxime Coquelin if (dev->max_rev >= 1) {
1148d1b4259fSMaxime Coquelin virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1);
1149d1b4259fSMaxime Coquelin }
1150d1b4259fSMaxime Coquelin }
1151d1b4259fSMaxime Coquelin
1152fb846a09SCornelia Huck /* This is called by virtio-bus just after the device is plugged. */
virtio_ccw_device_plugged(DeviceState * d,Error ** errp)1153e8398045SJason Wang static void virtio_ccw_device_plugged(DeviceState *d, Error **errp)
1154fb846a09SCornelia Huck {
1155fb846a09SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
115610ceaa1eSJason Wang VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1157b804e8a6SJing Liu CcwDevice *ccw_dev = CCW_DEVICE(d);
1158b804e8a6SJing Liu SubchDev *sch = ccw_dev->sch;
115910ceaa1eSJason Wang int n = virtio_get_num_queues(vdev);
11600708afa7SHalil Pasic S390FLICState *flic = s390_get_flic();
116110ceaa1eSJason Wang
1162d1b4259fSMaxime Coquelin if (!virtio_has_feature(vdev->host_features, VIRTIO_F_VERSION_1)) {
1163d1b4259fSMaxime Coquelin dev->max_rev = 0;
1164d1b4259fSMaxime Coquelin }
1165d1b4259fSMaxime Coquelin
11669b3a35ecSCornelia Huck if (!virtio_ccw_rev_max(dev) && !virtio_legacy_allowed(vdev)) {
1167d55f5182SStefano Garzarella /*
1168d55f5182SStefano Garzarella * To avoid migration issues, we allow legacy mode when legacy
1169d55f5182SStefano Garzarella * check is disabled in the old machine types (< 5.1).
1170d55f5182SStefano Garzarella */
1171d55f5182SStefano Garzarella if (virtio_legacy_check_disabled(vdev)) {
1172d55f5182SStefano Garzarella warn_report("device requires revision >= 1, but for backward "
1173d55f5182SStefano Garzarella "compatibility max_revision=0 is allowed");
1174d55f5182SStefano Garzarella } else {
11759b3a35ecSCornelia Huck error_setg(errp, "Invalid value of property max_rev "
11769b3a35ecSCornelia Huck "(is %d expected >= 1)", virtio_ccw_rev_max(dev));
11779b3a35ecSCornelia Huck return;
11789b3a35ecSCornelia Huck }
1179d55f5182SStefano Garzarella }
11809b3a35ecSCornelia Huck
1181b1914b82SHalil Pasic if (virtio_get_num_queues(vdev) > VIRTIO_QUEUE_MAX) {
1182b34aee54SMichael Tokarev error_setg(errp, "The number of virtqueues %d "
1183b1914b82SHalil Pasic "exceeds virtio limit %d", n,
1184b1914b82SHalil Pasic VIRTIO_QUEUE_MAX);
118510ceaa1eSJason Wang return;
118610ceaa1eSJason Wang }
11870708afa7SHalil Pasic if (virtio_get_num_queues(vdev) > flic->adapter_routes_max_batch) {
11880708afa7SHalil Pasic error_setg(errp, "The number of virtqueues %d "
11890708afa7SHalil Pasic "exceeds flic adapter route limit %d", n,
11900708afa7SHalil Pasic flic->adapter_routes_max_batch);
11910708afa7SHalil Pasic return;
11920708afa7SHalil Pasic }
1193fb846a09SCornelia Huck
1194fb846a09SCornelia Huck sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus);
1195fb846a09SCornelia Huck
1196542571d5SCornelia Huck
1197fb846a09SCornelia Huck css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid,
1198fb846a09SCornelia Huck d->hotplugged, 1);
1199fb846a09SCornelia Huck }
1200fb846a09SCornelia Huck
virtio_ccw_device_unplugged(DeviceState * d)1201fb846a09SCornelia Huck static void virtio_ccw_device_unplugged(DeviceState *d)
1202fb846a09SCornelia Huck {
1203fb846a09SCornelia Huck VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1204fb846a09SCornelia Huck
1205fb846a09SCornelia Huck virtio_ccw_stop_ioeventfd(dev);
1206fb846a09SCornelia Huck }
1207a5cf2bb4SCornelia Huck /**************** Virtio-ccw Bus Device Descriptions *******************/
1208a5cf2bb4SCornelia Huck
virtio_ccw_busdev_realize(DeviceState * dev,Error ** errp)12095e5ced38SMarkus Armbruster static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp)
1210a5cf2bb4SCornelia Huck {
1211a5cf2bb4SCornelia Huck VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
1212a5cf2bb4SCornelia Huck
12131bf4d7aaSAndreas Färber virtio_ccw_bus_new(&_dev->bus, sizeof(_dev->bus), _dev);
12141fa75523SCornelia Huck virtio_ccw_device_realize(_dev, errp);
1215a5cf2bb4SCornelia Huck }
1216a5cf2bb4SCornelia Huck
virtio_ccw_busdev_unrealize(DeviceState * dev)1217b69c3c21SMarkus Armbruster static void virtio_ccw_busdev_unrealize(DeviceState *dev)
1218a5cf2bb4SCornelia Huck {
1219a5cf2bb4SCornelia Huck VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
1220a5cf2bb4SCornelia Huck
1221b69c3c21SMarkus Armbruster virtio_ccw_device_unrealize(_dev);
1222a5cf2bb4SCornelia Huck }
1223a5cf2bb4SCornelia Huck
virtio_ccw_busdev_unplug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)1224b804e8a6SJing Liu static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev,
1225277bc95eSIgor Mammedov DeviceState *dev, Error **errp)
1226a5cf2bb4SCornelia Huck {
1227b804e8a6SJing Liu VirtioCcwDevice *_dev = to_virtio_ccw_dev_fast(dev);
1228a5cf2bb4SCornelia Huck
12290b81c1efSPaolo Bonzini virtio_ccw_stop_ioeventfd(_dev);
1230a5cf2bb4SCornelia Huck }
1231a5cf2bb4SCornelia Huck
virtio_ccw_device_class_init(ObjectClass * klass,void * data)1232a5cf2bb4SCornelia Huck static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
1233a5cf2bb4SCornelia Huck {
1234a5cf2bb4SCornelia Huck DeviceClass *dc = DEVICE_CLASS(klass);
1235b804e8a6SJing Liu CCWDeviceClass *k = CCW_DEVICE_CLASS(dc);
1236838fb84fSCornelia Huck VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_CLASS(klass);
1237*6affa00dSPeter Maydell ResettableClass *rc = RESETTABLE_CLASS(klass);
1238a5cf2bb4SCornelia Huck
1239b804e8a6SJing Liu k->unplug = virtio_ccw_busdev_unplug;
12405e5ced38SMarkus Armbruster dc->realize = virtio_ccw_busdev_realize;
124124118af8SNia Alarie dc->unrealize = virtio_ccw_busdev_unrealize;
1242*6affa00dSPeter Maydell resettable_class_set_parent_phases(rc, NULL, virtio_ccw_reset_hold, NULL,
1243*6affa00dSPeter Maydell &vdc->parent_phases);
1244a5cf2bb4SCornelia Huck }
1245a5cf2bb4SCornelia Huck
1246a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_device_info = {
1247a5cf2bb4SCornelia Huck .name = TYPE_VIRTIO_CCW_DEVICE,
1248b804e8a6SJing Liu .parent = TYPE_CCW_DEVICE,
1249a5cf2bb4SCornelia Huck .instance_size = sizeof(VirtioCcwDevice),
1250a5cf2bb4SCornelia Huck .class_init = virtio_ccw_device_class_init,
1251a5cf2bb4SCornelia Huck .class_size = sizeof(VirtIOCCWDeviceClass),
1252a5cf2bb4SCornelia Huck .abstract = true,
1253a5cf2bb4SCornelia Huck };
1254a5cf2bb4SCornelia Huck
1255a5cf2bb4SCornelia Huck /* virtio-ccw-bus */
1256a5cf2bb4SCornelia Huck
virtio_ccw_bus_new(VirtioBusState * bus,size_t bus_size,VirtioCcwDevice * dev)12571bf4d7aaSAndreas Färber static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
12581bf4d7aaSAndreas Färber VirtioCcwDevice *dev)
1259a5cf2bb4SCornelia Huck {
1260a5cf2bb4SCornelia Huck DeviceState *qdev = DEVICE(dev);
1261f4dd69aaSKONRAD Frederic char virtio_bus_name[] = "virtio-bus";
1262a5cf2bb4SCornelia Huck
1263d637e1dcSPeter Maydell qbus_init(bus, bus_size, TYPE_VIRTIO_CCW_BUS, qdev, virtio_bus_name);
1264a5cf2bb4SCornelia Huck }
1265a5cf2bb4SCornelia Huck
virtio_ccw_bus_class_init(ObjectClass * klass,void * data)1266a5cf2bb4SCornelia Huck static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
1267a5cf2bb4SCornelia Huck {
1268a5cf2bb4SCornelia Huck VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
1269a5cf2bb4SCornelia Huck BusClass *bus_class = BUS_CLASS(klass);
1270a5cf2bb4SCornelia Huck
1271a5cf2bb4SCornelia Huck bus_class->max_dev = 1;
1272a5cf2bb4SCornelia Huck k->notify = virtio_ccw_notify;
1273b4436a0bSCornelia Huck k->vmstate_change = virtio_ccw_vmstate_change;
1274320ce850SCornelia Huck k->query_guest_notifiers = virtio_ccw_query_guest_notifiers;
1275320ce850SCornelia Huck k->set_guest_notifiers = virtio_ccw_set_guest_notifiers;
1276bcb2b582SJens Freimann k->save_queue = virtio_ccw_save_queue;
1277bcb2b582SJens Freimann k->load_queue = virtio_ccw_load_queue;
1278bcb2b582SJens Freimann k->save_config = virtio_ccw_save_config;
1279bcb2b582SJens Freimann k->load_config = virtio_ccw_load_config;
1280d1b4259fSMaxime Coquelin k->pre_plugged = virtio_ccw_pre_plugged;
1281fb846a09SCornelia Huck k->device_plugged = virtio_ccw_device_plugged;
1282fb846a09SCornelia Huck k->device_unplugged = virtio_ccw_device_unplugged;
12838e93cef1SPaolo Bonzini k->ioeventfd_enabled = virtio_ccw_ioeventfd_enabled;
12847c55f68aSCornelia Huck k->ioeventfd_assign = virtio_ccw_ioeventfd_assign;
1285a5cf2bb4SCornelia Huck }
1286a5cf2bb4SCornelia Huck
1287a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_bus_info = {
1288a5cf2bb4SCornelia Huck .name = TYPE_VIRTIO_CCW_BUS,
1289a5cf2bb4SCornelia Huck .parent = TYPE_VIRTIO_BUS,
1290a5cf2bb4SCornelia Huck .instance_size = sizeof(VirtioCcwBusState),
129174ded8b4SCornelia Huck .class_size = sizeof(VirtioCcwBusClass),
1292a5cf2bb4SCornelia Huck .class_init = virtio_ccw_bus_class_init,
1293a5cf2bb4SCornelia Huck };
1294a5cf2bb4SCornelia Huck
virtio_ccw_register(void)1295a5cf2bb4SCornelia Huck static void virtio_ccw_register(void)
1296a5cf2bb4SCornelia Huck {
1297a5cf2bb4SCornelia Huck type_register_static(&virtio_ccw_bus_info);
1298a5cf2bb4SCornelia Huck type_register_static(&virtio_ccw_device_info);
1299a5cf2bb4SCornelia Huck }
1300a5cf2bb4SCornelia Huck
1301a5cf2bb4SCornelia Huck type_init(virtio_ccw_register)
1302