xref: /qemu/hw/s390x/virtio-ccw.c (revision b4f8f9df)
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 
13a5cf2bb4SCornelia Huck #include "hw/hw.h"
144be74634SMarkus Armbruster #include "sysemu/block-backend.h"
15a5cf2bb4SCornelia Huck #include "sysemu/blockdev.h"
16a5cf2bb4SCornelia Huck #include "sysemu/sysemu.h"
17a5cf2bb4SCornelia Huck #include "net/net.h"
180d09e41aSPaolo Bonzini #include "hw/virtio/virtio.h"
190d09e41aSPaolo Bonzini #include "hw/virtio/virtio-serial.h"
200d09e41aSPaolo Bonzini #include "hw/virtio/virtio-net.h"
21a5cf2bb4SCornelia Huck #include "hw/sysbus.h"
22a5cf2bb4SCornelia Huck #include "qemu/bitops.h"
23d49b6836SMarkus Armbruster #include "qemu/error-report.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 
29a5cf2bb4SCornelia Huck #include "ioinst.h"
30a5cf2bb4SCornelia Huck #include "css.h"
31a5cf2bb4SCornelia Huck #include "virtio-ccw.h"
32a5cf2bb4SCornelia Huck #include "trace.h"
33a5cf2bb4SCornelia Huck 
347bca3892SCornelia Huck static QTAILQ_HEAD(, IndAddr) indicator_addresses =
357bca3892SCornelia Huck     QTAILQ_HEAD_INITIALIZER(indicator_addresses);
367bca3892SCornelia Huck 
377bca3892SCornelia Huck static IndAddr *get_indicator(hwaddr ind_addr, int len)
387bca3892SCornelia Huck {
397bca3892SCornelia Huck     IndAddr *indicator;
407bca3892SCornelia Huck 
417bca3892SCornelia Huck     QTAILQ_FOREACH(indicator, &indicator_addresses, sibling) {
427bca3892SCornelia Huck         if (indicator->addr == ind_addr) {
437bca3892SCornelia Huck             indicator->refcnt++;
447bca3892SCornelia Huck             return indicator;
457bca3892SCornelia Huck         }
467bca3892SCornelia Huck     }
477bca3892SCornelia Huck     indicator = g_new0(IndAddr, 1);
487bca3892SCornelia Huck     indicator->addr = ind_addr;
497bca3892SCornelia Huck     indicator->len = len;
507bca3892SCornelia Huck     indicator->refcnt = 1;
517bca3892SCornelia Huck     QTAILQ_INSERT_TAIL(&indicator_addresses, indicator, sibling);
527bca3892SCornelia Huck     return indicator;
537bca3892SCornelia Huck }
547bca3892SCornelia Huck 
55d426d9fbSCornelia Huck static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr,
56d426d9fbSCornelia Huck                                bool do_map)
57d426d9fbSCornelia Huck {
58d426d9fbSCornelia Huck     S390FLICState *fs = s390_get_flic();
59d426d9fbSCornelia Huck     S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
60d426d9fbSCornelia Huck 
61d426d9fbSCornelia Huck     return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map);
62d426d9fbSCornelia Huck }
63d426d9fbSCornelia Huck 
64d426d9fbSCornelia Huck static void release_indicator(AdapterInfo *adapter, IndAddr *indicator)
657bca3892SCornelia Huck {
667bca3892SCornelia Huck     assert(indicator->refcnt > 0);
677bca3892SCornelia Huck     indicator->refcnt--;
687bca3892SCornelia Huck     if (indicator->refcnt > 0) {
697bca3892SCornelia Huck         return;
707bca3892SCornelia Huck     }
717bca3892SCornelia Huck     QTAILQ_REMOVE(&indicator_addresses, indicator, sibling);
72d426d9fbSCornelia Huck     if (indicator->map) {
73d426d9fbSCornelia Huck         s390_io_adapter_map(adapter, indicator->map, false);
74d426d9fbSCornelia Huck     }
757bca3892SCornelia Huck     g_free(indicator);
767bca3892SCornelia Huck }
777bca3892SCornelia Huck 
78d426d9fbSCornelia Huck static int map_indicator(AdapterInfo *adapter, IndAddr *indicator)
79d426d9fbSCornelia Huck {
80d426d9fbSCornelia Huck     int ret;
81d426d9fbSCornelia Huck 
82d426d9fbSCornelia Huck     if (indicator->map) {
83d426d9fbSCornelia Huck         return 0; /* already mapped is not an error */
84d426d9fbSCornelia Huck     }
85d426d9fbSCornelia Huck     indicator->map = indicator->addr;
86d426d9fbSCornelia Huck     ret = s390_io_adapter_map(adapter, indicator->map, true);
87d426d9fbSCornelia Huck     if ((ret != 0) && (ret != -ENOSYS)) {
88d426d9fbSCornelia Huck         goto out_err;
89d426d9fbSCornelia Huck     }
90d426d9fbSCornelia Huck     return 0;
91d426d9fbSCornelia Huck 
92d426d9fbSCornelia Huck out_err:
93d426d9fbSCornelia Huck     indicator->map = 0;
94d426d9fbSCornelia Huck     return ret;
95d426d9fbSCornelia Huck }
96d426d9fbSCornelia Huck 
971bf4d7aaSAndreas Färber static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
981bf4d7aaSAndreas Färber                                VirtioCcwDevice *dev);
99d51fcfacSKONRAD Frederic 
100dcc20931SPaolo Bonzini static void virtual_css_bus_reset(BusState *qbus)
101a5cf2bb4SCornelia Huck {
102a5cf2bb4SCornelia Huck     /* This should actually be modelled via the generic css */
103a5cf2bb4SCornelia Huck     css_reset();
104a5cf2bb4SCornelia Huck }
105a5cf2bb4SCornelia Huck 
106a5cf2bb4SCornelia Huck 
107a5cf2bb4SCornelia Huck static void virtual_css_bus_class_init(ObjectClass *klass, void *data)
108a5cf2bb4SCornelia Huck {
109a5cf2bb4SCornelia Huck     BusClass *k = BUS_CLASS(klass);
110a5cf2bb4SCornelia Huck 
111a5cf2bb4SCornelia Huck     k->reset = virtual_css_bus_reset;
112a5cf2bb4SCornelia Huck }
113a5cf2bb4SCornelia Huck 
114a5cf2bb4SCornelia Huck static const TypeInfo virtual_css_bus_info = {
115a5cf2bb4SCornelia Huck     .name = TYPE_VIRTUAL_CSS_BUS,
116a5cf2bb4SCornelia Huck     .parent = TYPE_BUS,
117a5cf2bb4SCornelia Huck     .instance_size = sizeof(VirtualCssBus),
118a5cf2bb4SCornelia Huck     .class_init = virtual_css_bus_class_init,
119a5cf2bb4SCornelia Huck };
120a5cf2bb4SCornelia Huck 
121a5cf2bb4SCornelia Huck VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
122a5cf2bb4SCornelia Huck {
123a5cf2bb4SCornelia Huck     VirtIODevice *vdev = NULL;
124f24a6840SPaolo Bonzini     VirtioCcwDevice *dev = sch->driver_data;
125a5cf2bb4SCornelia Huck 
126f24a6840SPaolo Bonzini     if (dev) {
127f24a6840SPaolo Bonzini         vdev = virtio_bus_get_device(&dev->bus);
128a5cf2bb4SCornelia Huck     }
129a5cf2bb4SCornelia Huck     return vdev;
130a5cf2bb4SCornelia Huck }
131a5cf2bb4SCornelia Huck 
132b4436a0bSCornelia Huck static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n,
133b4436a0bSCornelia Huck                                               bool assign, bool set_handler)
134b4436a0bSCornelia Huck {
135f24a6840SPaolo Bonzini     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
136f24a6840SPaolo Bonzini     VirtQueue *vq = virtio_get_queue(vdev, n);
137b4436a0bSCornelia Huck     EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
138b4436a0bSCornelia Huck     int r = 0;
139b4436a0bSCornelia Huck     SubchDev *sch = dev->sch;
140b4436a0bSCornelia Huck     uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid;
141b4436a0bSCornelia Huck 
142b4436a0bSCornelia Huck     if (assign) {
143b4436a0bSCornelia Huck         r = event_notifier_init(notifier, 1);
144b4436a0bSCornelia Huck         if (r < 0) {
145b4436a0bSCornelia Huck             error_report("%s: unable to init event notifier: %d", __func__, r);
146b4436a0bSCornelia Huck             return r;
147b4436a0bSCornelia Huck         }
148b4436a0bSCornelia Huck         virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
149cc3ac9c4SCornelia Huck         r = s390_assign_subch_ioeventfd(notifier, sch_id, n, assign);
150b4436a0bSCornelia Huck         if (r < 0) {
151b4436a0bSCornelia Huck             error_report("%s: unable to assign ioeventfd: %d", __func__, r);
152b4436a0bSCornelia Huck             virtio_queue_set_host_notifier_fd_handler(vq, false, false);
153b4436a0bSCornelia Huck             event_notifier_cleanup(notifier);
154b4436a0bSCornelia Huck             return r;
155b4436a0bSCornelia Huck         }
156b4436a0bSCornelia Huck     } else {
157b4436a0bSCornelia Huck         virtio_queue_set_host_notifier_fd_handler(vq, false, false);
158cc3ac9c4SCornelia Huck         s390_assign_subch_ioeventfd(notifier, sch_id, n, assign);
159b4436a0bSCornelia Huck         event_notifier_cleanup(notifier);
160b4436a0bSCornelia Huck     }
161b4436a0bSCornelia Huck     return r;
162b4436a0bSCornelia Huck }
163b4436a0bSCornelia Huck 
164b4436a0bSCornelia Huck static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
165b4436a0bSCornelia Huck {
166f24a6840SPaolo Bonzini     VirtIODevice *vdev;
167b4436a0bSCornelia Huck     int n, r;
168b4436a0bSCornelia Huck 
169b4436a0bSCornelia Huck     if (!(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) ||
170320ce850SCornelia Huck         dev->ioeventfd_disabled ||
171b4436a0bSCornelia Huck         dev->ioeventfd_started) {
172b4436a0bSCornelia Huck         return;
173b4436a0bSCornelia Huck     }
174f24a6840SPaolo Bonzini     vdev = virtio_bus_get_device(&dev->bus);
1758dfbaa6aSJason Wang     for (n = 0; n < VIRTIO_CCW_QUEUE_MAX; n++) {
176f24a6840SPaolo Bonzini         if (!virtio_queue_get_num(vdev, n)) {
177b4436a0bSCornelia Huck             continue;
178b4436a0bSCornelia Huck         }
179b4436a0bSCornelia Huck         r = virtio_ccw_set_guest2host_notifier(dev, n, true, true);
180b4436a0bSCornelia Huck         if (r < 0) {
181b4436a0bSCornelia Huck             goto assign_error;
182b4436a0bSCornelia Huck         }
183b4436a0bSCornelia Huck     }
184b4436a0bSCornelia Huck     dev->ioeventfd_started = true;
185b4436a0bSCornelia Huck     return;
186b4436a0bSCornelia Huck 
187b4436a0bSCornelia Huck   assign_error:
188b4436a0bSCornelia Huck     while (--n >= 0) {
189f24a6840SPaolo Bonzini         if (!virtio_queue_get_num(vdev, n)) {
190b4436a0bSCornelia Huck             continue;
191b4436a0bSCornelia Huck         }
192b4436a0bSCornelia Huck         r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
193b4436a0bSCornelia Huck         assert(r >= 0);
194b4436a0bSCornelia Huck     }
195b4436a0bSCornelia Huck     dev->ioeventfd_started = false;
196b4436a0bSCornelia Huck     /* Disable ioeventfd for this device. */
197b4436a0bSCornelia Huck     dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD;
198b4436a0bSCornelia Huck     error_report("%s: failed. Fallback to userspace (slower).", __func__);
199b4436a0bSCornelia Huck }
200b4436a0bSCornelia Huck 
201b4436a0bSCornelia Huck static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev)
202b4436a0bSCornelia Huck {
203f24a6840SPaolo Bonzini     VirtIODevice *vdev;
204b4436a0bSCornelia Huck     int n, r;
205b4436a0bSCornelia Huck 
206b4436a0bSCornelia Huck     if (!dev->ioeventfd_started) {
207b4436a0bSCornelia Huck         return;
208b4436a0bSCornelia Huck     }
209f24a6840SPaolo Bonzini     vdev = virtio_bus_get_device(&dev->bus);
2108dfbaa6aSJason Wang     for (n = 0; n < VIRTIO_CCW_QUEUE_MAX; n++) {
211f24a6840SPaolo Bonzini         if (!virtio_queue_get_num(vdev, n)) {
212b4436a0bSCornelia Huck             continue;
213b4436a0bSCornelia Huck         }
214b4436a0bSCornelia Huck         r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
215b4436a0bSCornelia Huck         assert(r >= 0);
216b4436a0bSCornelia Huck     }
217b4436a0bSCornelia Huck     dev->ioeventfd_started = false;
218b4436a0bSCornelia Huck }
219b4436a0bSCornelia Huck 
220a5cf2bb4SCornelia Huck VirtualCssBus *virtual_css_bus_init(void)
221a5cf2bb4SCornelia Huck {
222a5cf2bb4SCornelia Huck     VirtualCssBus *cbus;
223a5cf2bb4SCornelia Huck     BusState *bus;
224a5cf2bb4SCornelia Huck     DeviceState *dev;
225a5cf2bb4SCornelia Huck 
226a5cf2bb4SCornelia Huck     /* Create bridge device */
227a5cf2bb4SCornelia Huck     dev = qdev_create(NULL, "virtual-css-bridge");
228a5cf2bb4SCornelia Huck     qdev_init_nofail(dev);
229a5cf2bb4SCornelia Huck 
230a5cf2bb4SCornelia Huck     /* Create bus on bridge device */
231a5cf2bb4SCornelia Huck     bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css");
232a5cf2bb4SCornelia Huck     cbus = VIRTUAL_CSS_BUS(bus);
233a5cf2bb4SCornelia Huck 
234a5cf2bb4SCornelia Huck     /* Enable hotplugging */
235277bc95eSIgor Mammedov     qbus_set_hotplug_handler(bus, dev, &error_abort);
236a5cf2bb4SCornelia Huck 
237a5cf2bb4SCornelia Huck     return cbus;
238a5cf2bb4SCornelia Huck }
239a5cf2bb4SCornelia Huck 
240a5cf2bb4SCornelia Huck /* Communication blocks used by several channel commands. */
2410db87e0dSCornelia Huck typedef struct VqInfoBlockLegacy {
242a5cf2bb4SCornelia Huck     uint64_t queue;
243a5cf2bb4SCornelia Huck     uint32_t align;
244a5cf2bb4SCornelia Huck     uint16_t index;
245a5cf2bb4SCornelia Huck     uint16_t num;
2460db87e0dSCornelia Huck } QEMU_PACKED VqInfoBlockLegacy;
2470db87e0dSCornelia Huck 
2480db87e0dSCornelia Huck typedef struct VqInfoBlock {
2490db87e0dSCornelia Huck     uint64_t desc;
2500db87e0dSCornelia Huck     uint32_t res0;
2510db87e0dSCornelia Huck     uint16_t index;
2520db87e0dSCornelia Huck     uint16_t num;
2530db87e0dSCornelia Huck     uint64_t avail;
2540db87e0dSCornelia Huck     uint64_t used;
255a5cf2bb4SCornelia Huck } QEMU_PACKED VqInfoBlock;
256a5cf2bb4SCornelia Huck 
257a5cf2bb4SCornelia Huck typedef struct VqConfigBlock {
258a5cf2bb4SCornelia Huck     uint16_t index;
259a5cf2bb4SCornelia Huck     uint16_t num_max;
260a5cf2bb4SCornelia Huck } QEMU_PACKED VqConfigBlock;
261a5cf2bb4SCornelia Huck 
262a5cf2bb4SCornelia Huck typedef struct VirtioFeatDesc {
263a5cf2bb4SCornelia Huck     uint32_t features;
264a5cf2bb4SCornelia Huck     uint8_t index;
265a5cf2bb4SCornelia Huck } QEMU_PACKED VirtioFeatDesc;
266a5cf2bb4SCornelia Huck 
2677e749462SCornelia Huck typedef struct VirtioThinintInfo {
2687e749462SCornelia Huck     hwaddr summary_indicator;
2697e749462SCornelia Huck     hwaddr device_indicator;
2707e749462SCornelia Huck     uint64_t ind_bit;
2717e749462SCornelia Huck     uint8_t isc;
2727e749462SCornelia Huck } QEMU_PACKED VirtioThinintInfo;
2737e749462SCornelia Huck 
274c42767f2SThomas Huth typedef struct VirtioRevInfo {
275c42767f2SThomas Huth     uint16_t revision;
276c42767f2SThomas Huth     uint16_t length;
277c42767f2SThomas Huth     uint8_t data[0];
278c42767f2SThomas Huth } QEMU_PACKED VirtioRevInfo;
279c42767f2SThomas Huth 
280a5cf2bb4SCornelia Huck /* Specify where the virtqueues for the subchannel are in guest memory. */
2810db87e0dSCornelia Huck static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info,
2820db87e0dSCornelia Huck                               VqInfoBlockLegacy *linfo)
283a5cf2bb4SCornelia Huck {
284f24a6840SPaolo Bonzini     VirtIODevice *vdev = virtio_ccw_get_vdev(sch);
2850db87e0dSCornelia Huck     uint16_t index = info ? info->index : linfo->index;
2860db87e0dSCornelia Huck     uint16_t num = info ? info->num : linfo->num;
2870db87e0dSCornelia Huck     uint64_t desc = info ? info->desc : linfo->queue;
288a5cf2bb4SCornelia Huck 
2898dfbaa6aSJason Wang     if (index >= VIRTIO_CCW_QUEUE_MAX) {
290a5cf2bb4SCornelia Huck         return -EINVAL;
291a5cf2bb4SCornelia Huck     }
292a5cf2bb4SCornelia Huck 
293a5cf2bb4SCornelia Huck     /* Current code in virtio.c relies on 4K alignment. */
2940db87e0dSCornelia Huck     if (linfo && desc && (linfo->align != 4096)) {
295a5cf2bb4SCornelia Huck         return -EINVAL;
296a5cf2bb4SCornelia Huck     }
297a5cf2bb4SCornelia Huck 
298f24a6840SPaolo Bonzini     if (!vdev) {
299a5cf2bb4SCornelia Huck         return -EINVAL;
300a5cf2bb4SCornelia Huck     }
301a5cf2bb4SCornelia Huck 
3020db87e0dSCornelia Huck     if (info) {
3030db87e0dSCornelia Huck         virtio_queue_set_rings(vdev, index, desc, info->avail, info->used);
3040db87e0dSCornelia Huck     } else {
3050db87e0dSCornelia Huck         virtio_queue_set_addr(vdev, index, desc);
3060db87e0dSCornelia Huck     }
3070db87e0dSCornelia Huck     if (!desc) {
308955cc8c9SJason Wang         virtio_queue_set_vector(vdev, index, VIRTIO_NO_VECTOR);
309a5cf2bb4SCornelia Huck     } else {
31079cd0c80SCornelia Huck         if (info) {
31179cd0c80SCornelia Huck             /* virtio-1 allows changing the ring size. */
31279cd0c80SCornelia Huck             if (virtio_queue_get_num(vdev, index) < num) {
31379cd0c80SCornelia Huck                 /* Fail if we exceed the maximum number. */
314a5cf2bb4SCornelia Huck                 return -EINVAL;
315a5cf2bb4SCornelia Huck             }
31679cd0c80SCornelia Huck             virtio_queue_set_num(vdev, index, num);
31779cd0c80SCornelia Huck         } else if (virtio_queue_get_num(vdev, index) > num) {
31879cd0c80SCornelia Huck             /* Fail if we don't have a big enough queue. */
31979cd0c80SCornelia Huck             return -EINVAL;
32079cd0c80SCornelia Huck         }
32179cd0c80SCornelia Huck         /* We ignore possible increased num for legacy for compatibility. */
322f24a6840SPaolo Bonzini         virtio_queue_set_vector(vdev, index, index);
323a5cf2bb4SCornelia Huck     }
324a5cf2bb4SCornelia Huck     /* tell notify handler in case of config change */
3258dfbaa6aSJason Wang     vdev->config_vector = VIRTIO_CCW_QUEUE_MAX;
326a5cf2bb4SCornelia Huck     return 0;
327a5cf2bb4SCornelia Huck }
328a5cf2bb4SCornelia Huck 
329fa8b0ca5SCornelia Huck static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev)
330fa8b0ca5SCornelia Huck {
331fa8b0ca5SCornelia Huck     virtio_ccw_stop_ioeventfd(dev);
332fa8b0ca5SCornelia Huck     virtio_reset(vdev);
333fa8b0ca5SCornelia Huck     if (dev->indicators) {
334fa8b0ca5SCornelia Huck         release_indicator(&dev->routes.adapter, dev->indicators);
335fa8b0ca5SCornelia Huck         dev->indicators = NULL;
336fa8b0ca5SCornelia Huck     }
337fa8b0ca5SCornelia Huck     if (dev->indicators2) {
338fa8b0ca5SCornelia Huck         release_indicator(&dev->routes.adapter, dev->indicators2);
339fa8b0ca5SCornelia Huck         dev->indicators2 = NULL;
340fa8b0ca5SCornelia Huck     }
341fa8b0ca5SCornelia Huck     if (dev->summary_indicator) {
342fa8b0ca5SCornelia Huck         release_indicator(&dev->routes.adapter, dev->summary_indicator);
343fa8b0ca5SCornelia Huck         dev->summary_indicator = NULL;
344fa8b0ca5SCornelia Huck     }
345fa8b0ca5SCornelia Huck     dev->sch->thinint_active = false;
346fa8b0ca5SCornelia Huck }
347fa8b0ca5SCornelia Huck 
3480db87e0dSCornelia Huck static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len,
3490db87e0dSCornelia Huck                                     bool is_legacy)
350a5cf2bb4SCornelia Huck {
351a5cf2bb4SCornelia Huck     int ret;
352a5cf2bb4SCornelia Huck     VqInfoBlock info;
3530db87e0dSCornelia Huck     VqInfoBlockLegacy linfo;
3540db87e0dSCornelia Huck     size_t info_len = is_legacy ? sizeof(linfo) : sizeof(info);
3550db87e0dSCornelia Huck 
3560db87e0dSCornelia Huck     if (check_len) {
3570db87e0dSCornelia Huck         if (ccw.count != info_len) {
3580db87e0dSCornelia Huck             return -EINVAL;
3590db87e0dSCornelia Huck         }
3600db87e0dSCornelia Huck     } else if (ccw.count < info_len) {
3610db87e0dSCornelia Huck         /* Can't execute command. */
3620db87e0dSCornelia Huck         return -EINVAL;
3630db87e0dSCornelia Huck     }
3640db87e0dSCornelia Huck     if (!ccw.cda) {
3650db87e0dSCornelia Huck         return -EFAULT;
3660db87e0dSCornelia Huck     }
3670db87e0dSCornelia Huck     if (is_legacy) {
3680db87e0dSCornelia Huck         linfo.queue = address_space_ldq_be(&address_space_memory, ccw.cda,
3690db87e0dSCornelia Huck                                            MEMTXATTRS_UNSPECIFIED, NULL);
3700db87e0dSCornelia Huck         linfo.align = address_space_ldl_be(&address_space_memory,
3710db87e0dSCornelia Huck                                            ccw.cda + sizeof(linfo.queue),
3720db87e0dSCornelia Huck                                            MEMTXATTRS_UNSPECIFIED,
3730db87e0dSCornelia Huck                                            NULL);
3740db87e0dSCornelia Huck         linfo.index = address_space_lduw_be(&address_space_memory,
3750db87e0dSCornelia Huck                                             ccw.cda + sizeof(linfo.queue)
3760db87e0dSCornelia Huck                                             + sizeof(linfo.align),
3770db87e0dSCornelia Huck                                             MEMTXATTRS_UNSPECIFIED,
3780db87e0dSCornelia Huck                                             NULL);
3790db87e0dSCornelia Huck         linfo.num = address_space_lduw_be(&address_space_memory,
3800db87e0dSCornelia Huck                                           ccw.cda + sizeof(linfo.queue)
3810db87e0dSCornelia Huck                                           + sizeof(linfo.align)
3820db87e0dSCornelia Huck                                           + sizeof(linfo.index),
3830db87e0dSCornelia Huck                                           MEMTXATTRS_UNSPECIFIED,
3840db87e0dSCornelia Huck                                           NULL);
3850db87e0dSCornelia Huck         ret = virtio_ccw_set_vqs(sch, NULL, &linfo);
3860db87e0dSCornelia Huck     } else {
3870db87e0dSCornelia Huck         info.desc = address_space_ldq_be(&address_space_memory, ccw.cda,
3880db87e0dSCornelia Huck                                            MEMTXATTRS_UNSPECIFIED, NULL);
3890db87e0dSCornelia Huck         info.index = address_space_lduw_be(&address_space_memory,
3900db87e0dSCornelia Huck                                            ccw.cda + sizeof(info.desc)
3910db87e0dSCornelia Huck                                            + sizeof(info.res0),
3920db87e0dSCornelia Huck                                            MEMTXATTRS_UNSPECIFIED, NULL);
3930db87e0dSCornelia Huck         info.num = address_space_lduw_be(&address_space_memory,
3940db87e0dSCornelia Huck                                          ccw.cda + sizeof(info.desc)
3950db87e0dSCornelia Huck                                          + sizeof(info.res0)
3960db87e0dSCornelia Huck                                          + sizeof(info.index),
3970db87e0dSCornelia Huck                                          MEMTXATTRS_UNSPECIFIED, NULL);
3980db87e0dSCornelia Huck         info.avail = address_space_ldq_be(&address_space_memory,
3990db87e0dSCornelia Huck                                           ccw.cda + sizeof(info.desc)
4000db87e0dSCornelia Huck                                           + sizeof(info.res0)
4010db87e0dSCornelia Huck                                           + sizeof(info.index)
4020db87e0dSCornelia Huck                                           + sizeof(info.num),
4030db87e0dSCornelia Huck                                           MEMTXATTRS_UNSPECIFIED, NULL);
4040db87e0dSCornelia Huck         info.used = address_space_ldq_be(&address_space_memory,
4050db87e0dSCornelia Huck                                          ccw.cda + sizeof(info.desc)
4060db87e0dSCornelia Huck                                          + sizeof(info.res0)
4070db87e0dSCornelia Huck                                          + sizeof(info.index)
4080db87e0dSCornelia Huck                                          + sizeof(info.num)
4090db87e0dSCornelia Huck                                          + sizeof(info.avail),
4100db87e0dSCornelia Huck                                          MEMTXATTRS_UNSPECIFIED, NULL);
4110db87e0dSCornelia Huck         ret = virtio_ccw_set_vqs(sch, &info, NULL);
4120db87e0dSCornelia Huck     }
4130db87e0dSCornelia Huck     sch->curr_status.scsw.count = 0;
4140db87e0dSCornelia Huck     return ret;
4150db87e0dSCornelia Huck }
4160db87e0dSCornelia Huck 
4170db87e0dSCornelia Huck static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
4180db87e0dSCornelia Huck {
4190db87e0dSCornelia Huck     int ret;
420c42767f2SThomas Huth     VirtioRevInfo revinfo;
421a5cf2bb4SCornelia Huck     uint8_t status;
422a5cf2bb4SCornelia Huck     VirtioFeatDesc features;
423a5cf2bb4SCornelia Huck     void *config;
424a5cf2bb4SCornelia Huck     hwaddr indicators;
425a5cf2bb4SCornelia Huck     VqConfigBlock vq_config;
426a5cf2bb4SCornelia Huck     VirtioCcwDevice *dev = sch->driver_data;
427f24a6840SPaolo Bonzini     VirtIODevice *vdev = virtio_ccw_get_vdev(sch);
428a5cf2bb4SCornelia Huck     bool check_len;
429a5cf2bb4SCornelia Huck     int len;
430a5cf2bb4SCornelia Huck     hwaddr hw_len;
4317e749462SCornelia Huck     VirtioThinintInfo *thinint;
432a5cf2bb4SCornelia Huck 
433a5cf2bb4SCornelia Huck     if (!dev) {
434a5cf2bb4SCornelia Huck         return -EINVAL;
435a5cf2bb4SCornelia Huck     }
436a5cf2bb4SCornelia Huck 
437a5cf2bb4SCornelia Huck     trace_virtio_ccw_interpret_ccw(sch->cssid, sch->ssid, sch->schid,
438a5cf2bb4SCornelia Huck                                    ccw.cmd_code);
439a5cf2bb4SCornelia Huck     check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC));
440a5cf2bb4SCornelia Huck 
441a5cf2bb4SCornelia Huck     /* Look at the command. */
442a5cf2bb4SCornelia Huck     switch (ccw.cmd_code) {
443a5cf2bb4SCornelia Huck     case CCW_CMD_SET_VQ:
4440db87e0dSCornelia Huck         ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1);
445a5cf2bb4SCornelia Huck         break;
446a5cf2bb4SCornelia Huck     case CCW_CMD_VDEV_RESET:
447fa8b0ca5SCornelia Huck         virtio_ccw_reset_virtio(dev, vdev);
448a5cf2bb4SCornelia Huck         ret = 0;
449a5cf2bb4SCornelia Huck         break;
450a5cf2bb4SCornelia Huck     case CCW_CMD_READ_FEAT:
451a5cf2bb4SCornelia Huck         if (check_len) {
452a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(features)) {
453a5cf2bb4SCornelia Huck                 ret = -EINVAL;
454a5cf2bb4SCornelia Huck                 break;
455a5cf2bb4SCornelia Huck             }
456a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(features)) {
457a5cf2bb4SCornelia Huck             /* Can't execute command. */
458a5cf2bb4SCornelia Huck             ret = -EINVAL;
459a5cf2bb4SCornelia Huck             break;
460a5cf2bb4SCornelia Huck         }
461a5cf2bb4SCornelia Huck         if (!ccw.cda) {
462a5cf2bb4SCornelia Huck             ret = -EFAULT;
463a5cf2bb4SCornelia Huck         } else {
46442874d3aSPeter Maydell             features.index = address_space_ldub(&address_space_memory,
46542874d3aSPeter Maydell                                                 ccw.cda
46642874d3aSPeter Maydell                                                 + sizeof(features.features),
46742874d3aSPeter Maydell                                                 MEMTXATTRS_UNSPECIFIED,
46842874d3aSPeter Maydell                                                 NULL);
4696b8f1020SCornelia Huck             if (features.index == 0) {
470c42767f2SThomas Huth                 features.features = (uint32_t)vdev->host_features;
471*b4f8f9dfSCornelia Huck             } else if ((features.index == 1) && (dev->revision >= 1)) {
472c42767f2SThomas Huth                 /*
473*b4f8f9dfSCornelia Huck                  * Only offer feature bits beyond 31 if the guest has
474*b4f8f9dfSCornelia Huck                  * negotiated at least revision 1.
475c42767f2SThomas Huth                  */
476*b4f8f9dfSCornelia Huck                 features.features = (uint32_t)(vdev->host_features >> 32);
477a5cf2bb4SCornelia Huck             } else {
478a5cf2bb4SCornelia Huck                 /* Return zeroes if the guest supports more feature bits. */
479a5cf2bb4SCornelia Huck                 features.features = 0;
480a5cf2bb4SCornelia Huck             }
48142874d3aSPeter Maydell             address_space_stl_le(&address_space_memory, ccw.cda,
48242874d3aSPeter Maydell                                  features.features, MEMTXATTRS_UNSPECIFIED,
48342874d3aSPeter Maydell                                  NULL);
484a5cf2bb4SCornelia Huck             sch->curr_status.scsw.count = ccw.count - sizeof(features);
485a5cf2bb4SCornelia Huck             ret = 0;
486a5cf2bb4SCornelia Huck         }
487a5cf2bb4SCornelia Huck         break;
488a5cf2bb4SCornelia Huck     case CCW_CMD_WRITE_FEAT:
489a5cf2bb4SCornelia Huck         if (check_len) {
490a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(features)) {
491a5cf2bb4SCornelia Huck                 ret = -EINVAL;
492a5cf2bb4SCornelia Huck                 break;
493a5cf2bb4SCornelia Huck             }
494a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(features)) {
495a5cf2bb4SCornelia Huck             /* Can't execute command. */
496a5cf2bb4SCornelia Huck             ret = -EINVAL;
497a5cf2bb4SCornelia Huck             break;
498a5cf2bb4SCornelia Huck         }
499a5cf2bb4SCornelia Huck         if (!ccw.cda) {
500a5cf2bb4SCornelia Huck             ret = -EFAULT;
501a5cf2bb4SCornelia Huck         } else {
50242874d3aSPeter Maydell             features.index = address_space_ldub(&address_space_memory,
50342874d3aSPeter Maydell                                                 ccw.cda
50442874d3aSPeter Maydell                                                 + sizeof(features.features),
50542874d3aSPeter Maydell                                                 MEMTXATTRS_UNSPECIFIED,
50642874d3aSPeter Maydell                                                 NULL);
50742874d3aSPeter Maydell             features.features = address_space_ldl_le(&address_space_memory,
50842874d3aSPeter Maydell                                                      ccw.cda,
50942874d3aSPeter Maydell                                                      MEMTXATTRS_UNSPECIFIED,
51042874d3aSPeter Maydell                                                      NULL);
5116b8f1020SCornelia Huck             if (features.index == 0) {
512c42767f2SThomas Huth                 virtio_set_features(vdev,
513c42767f2SThomas Huth                                     (vdev->guest_features & 0xffffffff00000000ULL) |
514c42767f2SThomas Huth                                     features.features);
515*b4f8f9dfSCornelia Huck             } else if ((features.index == 1) && (dev->revision >= 1)) {
516c42767f2SThomas Huth                 /*
517*b4f8f9dfSCornelia Huck                  * If the guest did not negotiate at least revision 1,
518*b4f8f9dfSCornelia Huck                  * we did not offer it any feature bits beyond 31. Such a
519*b4f8f9dfSCornelia Huck                  * guest passing us any bit here is therefore buggy.
520c42767f2SThomas Huth                  */
521c42767f2SThomas Huth                 virtio_set_features(vdev,
522c42767f2SThomas Huth                                     (vdev->guest_features & 0x00000000ffffffffULL) |
523c42767f2SThomas Huth                                     ((uint64_t)features.features << 32));
524a5cf2bb4SCornelia Huck             } else {
525a5cf2bb4SCornelia Huck                 /*
526a5cf2bb4SCornelia Huck                  * If the guest supports more feature bits, assert that it
527a5cf2bb4SCornelia Huck                  * passes us zeroes for those we don't support.
528a5cf2bb4SCornelia Huck                  */
529a5cf2bb4SCornelia Huck                 if (features.features) {
530a5cf2bb4SCornelia Huck                     fprintf(stderr, "Guest bug: features[%i]=%x (expected 0)\n",
531a5cf2bb4SCornelia Huck                             features.index, features.features);
532a5cf2bb4SCornelia Huck                     /* XXX: do a unit check here? */
533a5cf2bb4SCornelia Huck                 }
534a5cf2bb4SCornelia Huck             }
535a5cf2bb4SCornelia Huck             sch->curr_status.scsw.count = ccw.count - sizeof(features);
536a5cf2bb4SCornelia Huck             ret = 0;
537a5cf2bb4SCornelia Huck         }
538a5cf2bb4SCornelia Huck         break;
539a5cf2bb4SCornelia Huck     case CCW_CMD_READ_CONF:
540a5cf2bb4SCornelia Huck         if (check_len) {
541f24a6840SPaolo Bonzini             if (ccw.count > vdev->config_len) {
542a5cf2bb4SCornelia Huck                 ret = -EINVAL;
543a5cf2bb4SCornelia Huck                 break;
544a5cf2bb4SCornelia Huck             }
545a5cf2bb4SCornelia Huck         }
546f24a6840SPaolo Bonzini         len = MIN(ccw.count, vdev->config_len);
547a5cf2bb4SCornelia Huck         if (!ccw.cda) {
548a5cf2bb4SCornelia Huck             ret = -EFAULT;
549a5cf2bb4SCornelia Huck         } else {
550f24a6840SPaolo Bonzini             virtio_bus_get_vdev_config(&dev->bus, vdev->config);
551a5cf2bb4SCornelia Huck             /* XXX config space endianness */
552f24a6840SPaolo Bonzini             cpu_physical_memory_write(ccw.cda, vdev->config, len);
553a5cf2bb4SCornelia Huck             sch->curr_status.scsw.count = ccw.count - len;
554a5cf2bb4SCornelia Huck             ret = 0;
555a5cf2bb4SCornelia Huck         }
556a5cf2bb4SCornelia Huck         break;
557a5cf2bb4SCornelia Huck     case CCW_CMD_WRITE_CONF:
558a5cf2bb4SCornelia Huck         if (check_len) {
559f24a6840SPaolo Bonzini             if (ccw.count > vdev->config_len) {
560a5cf2bb4SCornelia Huck                 ret = -EINVAL;
561a5cf2bb4SCornelia Huck                 break;
562a5cf2bb4SCornelia Huck             }
563a5cf2bb4SCornelia Huck         }
564f24a6840SPaolo Bonzini         len = MIN(ccw.count, vdev->config_len);
565a5cf2bb4SCornelia Huck         hw_len = len;
566a5cf2bb4SCornelia Huck         if (!ccw.cda) {
567a5cf2bb4SCornelia Huck             ret = -EFAULT;
568a5cf2bb4SCornelia Huck         } else {
569a5cf2bb4SCornelia Huck             config = cpu_physical_memory_map(ccw.cda, &hw_len, 0);
570a5cf2bb4SCornelia Huck             if (!config) {
571a5cf2bb4SCornelia Huck                 ret = -EFAULT;
572a5cf2bb4SCornelia Huck             } else {
573a5cf2bb4SCornelia Huck                 len = hw_len;
574a5cf2bb4SCornelia Huck                 /* XXX config space endianness */
575f24a6840SPaolo Bonzini                 memcpy(vdev->config, config, len);
576a5cf2bb4SCornelia Huck                 cpu_physical_memory_unmap(config, hw_len, 0, hw_len);
577f24a6840SPaolo Bonzini                 virtio_bus_set_vdev_config(&dev->bus, vdev->config);
578a5cf2bb4SCornelia Huck                 sch->curr_status.scsw.count = ccw.count - len;
579a5cf2bb4SCornelia Huck                 ret = 0;
580a5cf2bb4SCornelia Huck             }
581a5cf2bb4SCornelia Huck         }
582a5cf2bb4SCornelia Huck         break;
583a5cf2bb4SCornelia Huck     case CCW_CMD_WRITE_STATUS:
584a5cf2bb4SCornelia Huck         if (check_len) {
585a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(status)) {
586a5cf2bb4SCornelia Huck                 ret = -EINVAL;
587a5cf2bb4SCornelia Huck                 break;
588a5cf2bb4SCornelia Huck             }
589a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(status)) {
590a5cf2bb4SCornelia Huck             /* Can't execute command. */
591a5cf2bb4SCornelia Huck             ret = -EINVAL;
592a5cf2bb4SCornelia Huck             break;
593a5cf2bb4SCornelia Huck         }
594a5cf2bb4SCornelia Huck         if (!ccw.cda) {
595a5cf2bb4SCornelia Huck             ret = -EFAULT;
596a5cf2bb4SCornelia Huck         } else {
59742874d3aSPeter Maydell             status = address_space_ldub(&address_space_memory, ccw.cda,
59842874d3aSPeter Maydell                                         MEMTXATTRS_UNSPECIFIED, NULL);
599b4436a0bSCornelia Huck             if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
600b4436a0bSCornelia Huck                 virtio_ccw_stop_ioeventfd(dev);
601b4436a0bSCornelia Huck             }
6020b352fd6SCornelia Huck             if (virtio_set_status(vdev, status) == 0) {
603f24a6840SPaolo Bonzini                 if (vdev->status == 0) {
604fa8b0ca5SCornelia Huck                     virtio_ccw_reset_virtio(dev, vdev);
605a5cf2bb4SCornelia Huck                 }
606b4436a0bSCornelia Huck                 if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
607b4436a0bSCornelia Huck                     virtio_ccw_start_ioeventfd(dev);
608b4436a0bSCornelia Huck                 }
609a5cf2bb4SCornelia Huck                 sch->curr_status.scsw.count = ccw.count - sizeof(status);
610a5cf2bb4SCornelia Huck                 ret = 0;
6110b352fd6SCornelia Huck             } else {
6120b352fd6SCornelia Huck                 /* Trigger a command reject. */
6130b352fd6SCornelia Huck                 ret = -ENOSYS;
6140b352fd6SCornelia Huck             }
615a5cf2bb4SCornelia Huck         }
616a5cf2bb4SCornelia Huck         break;
617a5cf2bb4SCornelia Huck     case CCW_CMD_SET_IND:
618a5cf2bb4SCornelia Huck         if (check_len) {
619a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(indicators)) {
620a5cf2bb4SCornelia Huck                 ret = -EINVAL;
621a5cf2bb4SCornelia Huck                 break;
622a5cf2bb4SCornelia Huck             }
623a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(indicators)) {
624a5cf2bb4SCornelia Huck             /* Can't execute command. */
625a5cf2bb4SCornelia Huck             ret = -EINVAL;
626a5cf2bb4SCornelia Huck             break;
627a5cf2bb4SCornelia Huck         }
6287e749462SCornelia Huck         if (sch->thinint_active) {
6297e749462SCornelia Huck             /* Trigger a command reject. */
6307e749462SCornelia Huck             ret = -ENOSYS;
6317e749462SCornelia Huck             break;
6327e749462SCornelia Huck         }
633d1db1fa8SCornelia Huck         if (!ccw.cda) {
634a5cf2bb4SCornelia Huck             ret = -EFAULT;
635a5cf2bb4SCornelia Huck         } else {
63642874d3aSPeter Maydell             indicators = address_space_ldq_be(&address_space_memory, ccw.cda,
63742874d3aSPeter Maydell                                               MEMTXATTRS_UNSPECIFIED, NULL);
6387bca3892SCornelia Huck             dev->indicators = get_indicator(indicators, sizeof(uint64_t));
639a5cf2bb4SCornelia Huck             sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
640a5cf2bb4SCornelia Huck             ret = 0;
641a5cf2bb4SCornelia Huck         }
642a5cf2bb4SCornelia Huck         break;
643a5cf2bb4SCornelia Huck     case CCW_CMD_SET_CONF_IND:
644a5cf2bb4SCornelia Huck         if (check_len) {
645a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(indicators)) {
646a5cf2bb4SCornelia Huck                 ret = -EINVAL;
647a5cf2bb4SCornelia Huck                 break;
648a5cf2bb4SCornelia Huck             }
649a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(indicators)) {
650a5cf2bb4SCornelia Huck             /* Can't execute command. */
651a5cf2bb4SCornelia Huck             ret = -EINVAL;
652a5cf2bb4SCornelia Huck             break;
653a5cf2bb4SCornelia Huck         }
654d1db1fa8SCornelia Huck         if (!ccw.cda) {
655a5cf2bb4SCornelia Huck             ret = -EFAULT;
656a5cf2bb4SCornelia Huck         } else {
65742874d3aSPeter Maydell             indicators = address_space_ldq_be(&address_space_memory, ccw.cda,
65842874d3aSPeter Maydell                                               MEMTXATTRS_UNSPECIFIED, NULL);
6597bca3892SCornelia Huck             dev->indicators2 = get_indicator(indicators, sizeof(uint64_t));
660a5cf2bb4SCornelia Huck             sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
661a5cf2bb4SCornelia Huck             ret = 0;
662a5cf2bb4SCornelia Huck         }
663a5cf2bb4SCornelia Huck         break;
664a5cf2bb4SCornelia Huck     case CCW_CMD_READ_VQ_CONF:
665a5cf2bb4SCornelia Huck         if (check_len) {
666a5cf2bb4SCornelia Huck             if (ccw.count != sizeof(vq_config)) {
667a5cf2bb4SCornelia Huck                 ret = -EINVAL;
668a5cf2bb4SCornelia Huck                 break;
669a5cf2bb4SCornelia Huck             }
670a5cf2bb4SCornelia Huck         } else if (ccw.count < sizeof(vq_config)) {
671a5cf2bb4SCornelia Huck             /* Can't execute command. */
672a5cf2bb4SCornelia Huck             ret = -EINVAL;
673a5cf2bb4SCornelia Huck             break;
674a5cf2bb4SCornelia Huck         }
675a5cf2bb4SCornelia Huck         if (!ccw.cda) {
676a5cf2bb4SCornelia Huck             ret = -EFAULT;
677a5cf2bb4SCornelia Huck         } else {
67842874d3aSPeter Maydell             vq_config.index = address_space_lduw_be(&address_space_memory,
67942874d3aSPeter Maydell                                                     ccw.cda,
68042874d3aSPeter Maydell                                                     MEMTXATTRS_UNSPECIFIED,
68142874d3aSPeter Maydell                                                     NULL);
6828dfbaa6aSJason Wang             if (vq_config.index >= VIRTIO_CCW_QUEUE_MAX) {
683d03a3630SCornelia Huck                 ret = -EINVAL;
684d03a3630SCornelia Huck                 break;
685d03a3630SCornelia Huck             }
686f24a6840SPaolo Bonzini             vq_config.num_max = virtio_queue_get_num(vdev,
687a5cf2bb4SCornelia Huck                                                      vq_config.index);
68842874d3aSPeter Maydell             address_space_stw_be(&address_space_memory,
68942874d3aSPeter Maydell                                  ccw.cda + sizeof(vq_config.index),
69042874d3aSPeter Maydell                                  vq_config.num_max,
69142874d3aSPeter Maydell                                  MEMTXATTRS_UNSPECIFIED,
69242874d3aSPeter Maydell                                  NULL);
693a5cf2bb4SCornelia Huck             sch->curr_status.scsw.count = ccw.count - sizeof(vq_config);
694a5cf2bb4SCornelia Huck             ret = 0;
695a5cf2bb4SCornelia Huck         }
696a5cf2bb4SCornelia Huck         break;
6977e749462SCornelia Huck     case CCW_CMD_SET_IND_ADAPTER:
6987e749462SCornelia Huck         if (check_len) {
6997e749462SCornelia Huck             if (ccw.count != sizeof(*thinint)) {
7007e749462SCornelia Huck                 ret = -EINVAL;
7017e749462SCornelia Huck                 break;
7027e749462SCornelia Huck             }
7037e749462SCornelia Huck         } else if (ccw.count < sizeof(*thinint)) {
7047e749462SCornelia Huck             /* Can't execute command. */
7057e749462SCornelia Huck             ret = -EINVAL;
7067e749462SCornelia Huck             break;
7077e749462SCornelia Huck         }
7087e749462SCornelia Huck         len = sizeof(*thinint);
7097e749462SCornelia Huck         hw_len = len;
7107e749462SCornelia Huck         if (!ccw.cda) {
7117e749462SCornelia Huck             ret = -EFAULT;
7127e749462SCornelia Huck         } else if (dev->indicators && !sch->thinint_active) {
7137e749462SCornelia Huck             /* Trigger a command reject. */
7147e749462SCornelia Huck             ret = -ENOSYS;
7157e749462SCornelia Huck         } else {
7167e749462SCornelia Huck             thinint = cpu_physical_memory_map(ccw.cda, &hw_len, 0);
7177e749462SCornelia Huck             if (!thinint) {
7187e749462SCornelia Huck                 ret = -EFAULT;
7197e749462SCornelia Huck             } else {
7207d45285fSCornelia Huck                 uint64_t ind_bit = ldq_be_p(&thinint->ind_bit);
7217d45285fSCornelia Huck 
7227e749462SCornelia Huck                 len = hw_len;
7237bca3892SCornelia Huck                 dev->summary_indicator =
7247d45285fSCornelia Huck                     get_indicator(ldq_be_p(&thinint->summary_indicator),
7257d45285fSCornelia Huck                                   sizeof(uint8_t));
7267d45285fSCornelia Huck                 dev->indicators =
7277d45285fSCornelia Huck                     get_indicator(ldq_be_p(&thinint->device_indicator),
7287d45285fSCornelia Huck                                   ind_bit / 8 + 1);
7297e749462SCornelia Huck                 dev->thinint_isc = thinint->isc;
7307d45285fSCornelia Huck                 dev->routes.adapter.ind_offset = ind_bit;
731d426d9fbSCornelia Huck                 dev->routes.adapter.summary_offset = 7;
7327e749462SCornelia Huck                 cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len);
73303cf077aSCornelia Huck                 ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO,
73403cf077aSCornelia Huck                                               dev->thinint_isc, true, false,
735d426d9fbSCornelia Huck                                               &dev->routes.adapter.adapter_id);
73603cf077aSCornelia Huck                 assert(ret == 0);
7377bca3892SCornelia Huck                 sch->thinint_active = ((dev->indicators != NULL) &&
7387bca3892SCornelia Huck                                        (dev->summary_indicator != NULL));
7397e749462SCornelia Huck                 sch->curr_status.scsw.count = ccw.count - len;
7407e749462SCornelia Huck                 ret = 0;
7417e749462SCornelia Huck             }
7427e749462SCornelia Huck         }
7437e749462SCornelia Huck         break;
744c42767f2SThomas Huth     case CCW_CMD_SET_VIRTIO_REV:
745c42767f2SThomas Huth         len = sizeof(revinfo);
746c42767f2SThomas Huth         if (ccw.count < len) {
747c42767f2SThomas Huth             ret = -EINVAL;
748c42767f2SThomas Huth             break;
749c42767f2SThomas Huth         }
750c42767f2SThomas Huth         if (!ccw.cda) {
751c42767f2SThomas Huth             ret = -EFAULT;
752c42767f2SThomas Huth             break;
753c42767f2SThomas Huth         }
754c42767f2SThomas Huth         revinfo.revision =
755c42767f2SThomas Huth             address_space_lduw_be(&address_space_memory, ccw.cda,
756c42767f2SThomas Huth                                   MEMTXATTRS_UNSPECIFIED, NULL);
757c42767f2SThomas Huth         revinfo.length =
758c42767f2SThomas Huth             address_space_lduw_be(&address_space_memory,
759c42767f2SThomas Huth                                   ccw.cda + sizeof(revinfo.revision),
760c42767f2SThomas Huth                                   MEMTXATTRS_UNSPECIFIED, NULL);
761c42767f2SThomas Huth         if (ccw.count < len + revinfo.length ||
762c42767f2SThomas Huth             (check_len && ccw.count > len + revinfo.length)) {
763c42767f2SThomas Huth             ret = -EINVAL;
764c42767f2SThomas Huth             break;
765c42767f2SThomas Huth         }
766c42767f2SThomas Huth         /*
767c42767f2SThomas Huth          * Once we start to support revisions with additional data, we'll
768c42767f2SThomas Huth          * need to fetch it here. Nothing to do for now, though.
769c42767f2SThomas Huth          */
770c42767f2SThomas Huth         if (dev->revision >= 0 ||
771c42767f2SThomas Huth             revinfo.revision > virtio_ccw_rev_max(vdev)) {
772c42767f2SThomas Huth             ret = -ENOSYS;
773c42767f2SThomas Huth             break;
774c42767f2SThomas Huth         }
775c42767f2SThomas Huth         ret = 0;
776c42767f2SThomas Huth         dev->revision = revinfo.revision;
777c42767f2SThomas Huth         break;
778a5cf2bb4SCornelia Huck     default:
7798d034a6fSCornelia Huck         ret = -ENOSYS;
780a5cf2bb4SCornelia Huck         break;
781a5cf2bb4SCornelia Huck     }
782a5cf2bb4SCornelia Huck     return ret;
783a5cf2bb4SCornelia Huck }
784a5cf2bb4SCornelia Huck 
785c42767f2SThomas Huth static void virtio_sch_disable_cb(SubchDev *sch)
786c42767f2SThomas Huth {
787c42767f2SThomas Huth     VirtioCcwDevice *dev = sch->driver_data;
788c42767f2SThomas Huth 
789c42767f2SThomas Huth     dev->revision = -1;
790c42767f2SThomas Huth }
791c42767f2SThomas Huth 
7921fa75523SCornelia Huck static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp)
793a5cf2bb4SCornelia Huck {
794a5cf2bb4SCornelia Huck     unsigned int cssid = 0;
795a5cf2bb4SCornelia Huck     unsigned int ssid = 0;
796a5cf2bb4SCornelia Huck     unsigned int schid;
797a5cf2bb4SCornelia Huck     unsigned int devno;
798a5cf2bb4SCornelia Huck     bool have_devno = false;
799a5cf2bb4SCornelia Huck     bool found = false;
800a5cf2bb4SCornelia Huck     SubchDev *sch;
801a5cf2bb4SCornelia Huck     int num;
8021fa75523SCornelia Huck     Error *err = NULL;
8031fa75523SCornelia Huck     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
804a5cf2bb4SCornelia Huck 
805a5cf2bb4SCornelia Huck     sch = g_malloc0(sizeof(SubchDev));
806a5cf2bb4SCornelia Huck 
807a5cf2bb4SCornelia Huck     sch->driver_data = dev;
808a5cf2bb4SCornelia Huck     dev->sch = sch;
809a5cf2bb4SCornelia Huck 
8107bca3892SCornelia Huck     dev->indicators = NULL;
811a5cf2bb4SCornelia Huck 
812a5cf2bb4SCornelia Huck     /* Initialize subchannel structure. */
813a5cf2bb4SCornelia Huck     sch->channel_prog = 0x0;
814a5cf2bb4SCornelia Huck     sch->last_cmd_valid = false;
8157e749462SCornelia Huck     sch->thinint_active = false;
816a5cf2bb4SCornelia Huck     /*
817a5cf2bb4SCornelia Huck      * Use a device number if provided. Otherwise, fall back to subchannel
818a5cf2bb4SCornelia Huck      * number.
819a5cf2bb4SCornelia Huck      */
820a5cf2bb4SCornelia Huck     if (dev->bus_id) {
821a5cf2bb4SCornelia Huck         num = sscanf(dev->bus_id, "%x.%x.%04x", &cssid, &ssid, &devno);
822a5cf2bb4SCornelia Huck         if (num == 3) {
823a5cf2bb4SCornelia Huck             if ((cssid > MAX_CSSID) || (ssid > MAX_SSID)) {
8245e5ced38SMarkus Armbruster                 error_setg(errp, "Invalid cssid or ssid: cssid %x, ssid %x",
825a5cf2bb4SCornelia Huck                            cssid, ssid);
826a5cf2bb4SCornelia Huck                 goto out_err;
827a5cf2bb4SCornelia Huck             }
828a5cf2bb4SCornelia Huck             /* Enforce use of virtual cssid. */
829a5cf2bb4SCornelia Huck             if (cssid != VIRTUAL_CSSID) {
8305e5ced38SMarkus Armbruster                 error_setg(errp, "cssid %x not valid for virtio devices",
8315e5ced38SMarkus Armbruster                            cssid);
832a5cf2bb4SCornelia Huck                 goto out_err;
833a5cf2bb4SCornelia Huck             }
834a5cf2bb4SCornelia Huck             if (css_devno_used(cssid, ssid, devno)) {
8355e5ced38SMarkus Armbruster                 error_setg(errp, "Device %x.%x.%04x already exists",
8365e5ced38SMarkus Armbruster                            cssid, ssid, devno);
837a5cf2bb4SCornelia Huck                 goto out_err;
838a5cf2bb4SCornelia Huck             }
839a5cf2bb4SCornelia Huck             sch->cssid = cssid;
840a5cf2bb4SCornelia Huck             sch->ssid = ssid;
841a5cf2bb4SCornelia Huck             sch->devno = devno;
842a5cf2bb4SCornelia Huck             have_devno = true;
843a5cf2bb4SCornelia Huck         } else {
8445e5ced38SMarkus Armbruster             error_setg(errp, "Malformed devno parameter '%s'", dev->bus_id);
845a5cf2bb4SCornelia Huck             goto out_err;
846a5cf2bb4SCornelia Huck         }
847a5cf2bb4SCornelia Huck     }
848a5cf2bb4SCornelia Huck 
849a5cf2bb4SCornelia Huck     /* Find the next free id. */
850a5cf2bb4SCornelia Huck     if (have_devno) {
851a5cf2bb4SCornelia Huck         for (schid = 0; schid <= MAX_SCHID; schid++) {
852a5cf2bb4SCornelia Huck             if (!css_find_subch(1, cssid, ssid, schid)) {
853a5cf2bb4SCornelia Huck                 sch->schid = schid;
854a5cf2bb4SCornelia Huck                 css_subch_assign(cssid, ssid, schid, devno, sch);
855a5cf2bb4SCornelia Huck                 found = true;
856a5cf2bb4SCornelia Huck                 break;
857a5cf2bb4SCornelia Huck             }
858a5cf2bb4SCornelia Huck         }
859a5cf2bb4SCornelia Huck         if (!found) {
8605e5ced38SMarkus Armbruster             error_setg(errp, "No free subchannel found for %x.%x.%04x",
8615e5ced38SMarkus Armbruster                        cssid, ssid, devno);
862a5cf2bb4SCornelia Huck             goto out_err;
863a5cf2bb4SCornelia Huck         }
864a5cf2bb4SCornelia Huck         trace_virtio_ccw_new_device(cssid, ssid, schid, devno,
865a5cf2bb4SCornelia Huck                                     "user-configured");
866a5cf2bb4SCornelia Huck     } else {
867a5cf2bb4SCornelia Huck         cssid = VIRTUAL_CSSID;
868a5cf2bb4SCornelia Huck         for (ssid = 0; ssid <= MAX_SSID; ssid++) {
869a5cf2bb4SCornelia Huck             for (schid = 0; schid <= MAX_SCHID; schid++) {
870a5cf2bb4SCornelia Huck                 if (!css_find_subch(1, cssid, ssid, schid)) {
871a5cf2bb4SCornelia Huck                     sch->cssid = cssid;
872a5cf2bb4SCornelia Huck                     sch->ssid = ssid;
873a5cf2bb4SCornelia Huck                     sch->schid = schid;
874a5cf2bb4SCornelia Huck                     devno = schid;
875a5cf2bb4SCornelia Huck                     /*
876a5cf2bb4SCornelia Huck                      * If the devno is already taken, look further in this
877a5cf2bb4SCornelia Huck                      * subchannel set.
878a5cf2bb4SCornelia Huck                      */
879a5cf2bb4SCornelia Huck                     while (css_devno_used(cssid, ssid, devno)) {
880a5cf2bb4SCornelia Huck                         if (devno == MAX_SCHID) {
881a5cf2bb4SCornelia Huck                             devno = 0;
882a5cf2bb4SCornelia Huck                         } else if (devno == schid - 1) {
8835e5ced38SMarkus Armbruster                             error_setg(errp, "No free devno found");
884a5cf2bb4SCornelia Huck                             goto out_err;
885a5cf2bb4SCornelia Huck                         } else {
886a5cf2bb4SCornelia Huck                             devno++;
887a5cf2bb4SCornelia Huck                         }
888a5cf2bb4SCornelia Huck                     }
889a5cf2bb4SCornelia Huck                     sch->devno = devno;
890a5cf2bb4SCornelia Huck                     css_subch_assign(cssid, ssid, schid, devno, sch);
891a5cf2bb4SCornelia Huck                     found = true;
892a5cf2bb4SCornelia Huck                     break;
893a5cf2bb4SCornelia Huck                 }
894a5cf2bb4SCornelia Huck             }
895a5cf2bb4SCornelia Huck             if (found) {
896a5cf2bb4SCornelia Huck                 break;
897a5cf2bb4SCornelia Huck             }
898a5cf2bb4SCornelia Huck         }
899a5cf2bb4SCornelia Huck         if (!found) {
9005e5ced38SMarkus Armbruster             error_setg(errp, "Virtual channel subsystem is full!");
901a5cf2bb4SCornelia Huck             goto out_err;
902a5cf2bb4SCornelia Huck         }
903a5cf2bb4SCornelia Huck         trace_virtio_ccw_new_device(cssid, ssid, schid, devno,
904a5cf2bb4SCornelia Huck                                     "auto-configured");
905a5cf2bb4SCornelia Huck     }
906a5cf2bb4SCornelia Huck 
907a5cf2bb4SCornelia Huck     /* Build initial schib. */
908a5cf2bb4SCornelia Huck     css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE);
909a5cf2bb4SCornelia Huck 
910a5cf2bb4SCornelia Huck     sch->ccw_cb = virtio_ccw_cb;
911c42767f2SThomas Huth     sch->disable_cb = virtio_sch_disable_cb;
912a5cf2bb4SCornelia Huck 
913a5cf2bb4SCornelia Huck     /* Build senseid data. */
914a5cf2bb4SCornelia Huck     memset(&sch->id, 0, sizeof(SenseId));
915a5cf2bb4SCornelia Huck     sch->id.reserved = 0xff;
916a5cf2bb4SCornelia Huck     sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
9171fa75523SCornelia Huck 
918c42767f2SThomas Huth     dev->revision = -1;
919c42767f2SThomas Huth 
9201fa75523SCornelia Huck     if (k->realize) {
9211fa75523SCornelia Huck         k->realize(dev, &err);
9221fa75523SCornelia Huck     }
9231fa75523SCornelia Huck     if (err) {
9241fa75523SCornelia Huck         error_propagate(errp, err);
9251fa75523SCornelia Huck         css_subch_assign(cssid, ssid, schid, devno, NULL);
9261fa75523SCornelia Huck         goto out_err;
9271fa75523SCornelia Huck     }
9281fa75523SCornelia Huck 
9295e5ced38SMarkus Armbruster     return;
930a5cf2bb4SCornelia Huck 
931a5cf2bb4SCornelia Huck out_err:
932a5cf2bb4SCornelia Huck     dev->sch = NULL;
933a5cf2bb4SCornelia Huck     g_free(sch);
934a5cf2bb4SCornelia Huck }
935a5cf2bb4SCornelia Huck 
936a5cf2bb4SCornelia Huck static int virtio_ccw_exit(VirtioCcwDevice *dev)
937a5cf2bb4SCornelia Huck {
938a5cf2bb4SCornelia Huck     SubchDev *sch = dev->sch;
939a5cf2bb4SCornelia Huck 
940a5cf2bb4SCornelia Huck     if (sch) {
941a5cf2bb4SCornelia Huck         css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
942a5cf2bb4SCornelia Huck         g_free(sch);
943a5cf2bb4SCornelia Huck     }
9447bca3892SCornelia Huck     if (dev->indicators) {
945d426d9fbSCornelia Huck         release_indicator(&dev->routes.adapter, dev->indicators);
9467bca3892SCornelia Huck         dev->indicators = NULL;
9477bca3892SCornelia Huck     }
948a5cf2bb4SCornelia Huck     return 0;
949a5cf2bb4SCornelia Huck }
950a5cf2bb4SCornelia Huck 
9515e5ced38SMarkus Armbruster static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp)
952a5cf2bb4SCornelia Huck {
953800ced8cSKONRAD Frederic     DeviceState *qdev = DEVICE(ccw_dev);
95489334c8bSKONRAD Frederic     VirtIONetCcw *dev = VIRTIO_NET_CCW(ccw_dev);
95589334c8bSKONRAD Frederic     DeviceState *vdev = DEVICE(&dev->vdev);
9565e5ced38SMarkus Armbruster     Error *err = NULL;
957a5cf2bb4SCornelia Huck 
958800ced8cSKONRAD Frederic     virtio_net_set_netclient_name(&dev->vdev, qdev->id,
959800ced8cSKONRAD Frederic                                   object_get_typename(OBJECT(qdev)));
96089334c8bSKONRAD Frederic     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
9615e5ced38SMarkus Armbruster     object_property_set_bool(OBJECT(vdev), true, "realized", &err);
9625e5ced38SMarkus Armbruster     if (err) {
9635e5ced38SMarkus Armbruster         error_propagate(errp, err);
964a5cf2bb4SCornelia Huck     }
965a5cf2bb4SCornelia Huck }
966a5cf2bb4SCornelia Huck 
96789334c8bSKONRAD Frederic static void virtio_ccw_net_instance_init(Object *obj)
968a5cf2bb4SCornelia Huck {
96989334c8bSKONRAD Frederic     VirtIONetCcw *dev = VIRTIO_NET_CCW(obj);
970c8075cafSGonglei 
971c8075cafSGonglei     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
972c8075cafSGonglei                                 TYPE_VIRTIO_NET);
9730cf63c3eSGonglei     object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
9740cf63c3eSGonglei                               "bootindex", &error_abort);
975a5cf2bb4SCornelia Huck }
976a5cf2bb4SCornelia Huck 
9775e5ced38SMarkus Armbruster static void virtio_ccw_blk_realize(VirtioCcwDevice *ccw_dev, Error **errp)
978a5cf2bb4SCornelia Huck {
9793400c455SKONRAD Frederic     VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(ccw_dev);
9803400c455SKONRAD Frederic     DeviceState *vdev = DEVICE(&dev->vdev);
9815e5ced38SMarkus Armbruster     Error *err = NULL;
9825e5ced38SMarkus Armbruster 
9833400c455SKONRAD Frederic     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
9845e5ced38SMarkus Armbruster     object_property_set_bool(OBJECT(vdev), true, "realized", &err);
9855e5ced38SMarkus Armbruster     if (err) {
9865e5ced38SMarkus Armbruster         error_propagate(errp, err);
987a5cf2bb4SCornelia Huck     }
988a5cf2bb4SCornelia Huck }
989a5cf2bb4SCornelia Huck 
9903400c455SKONRAD Frederic static void virtio_ccw_blk_instance_init(Object *obj)
991a5cf2bb4SCornelia Huck {
9923400c455SKONRAD Frederic     VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(obj);
993c8075cafSGonglei 
994c8075cafSGonglei     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
995c8075cafSGonglei                                 TYPE_VIRTIO_BLK);
996467b3f33SStefan Hajnoczi     object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread",
997467b3f33SStefan Hajnoczi                               &error_abort);
998aeb98ddcSGonglei     object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
999aeb98ddcSGonglei                               "bootindex", &error_abort);
1000a5cf2bb4SCornelia Huck }
1001a5cf2bb4SCornelia Huck 
10025e5ced38SMarkus Armbruster static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp)
1003a5cf2bb4SCornelia Huck {
10046acf69cdSKONRAD Frederic     VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(ccw_dev);
10056acf69cdSKONRAD Frederic     DeviceState *vdev = DEVICE(&dev->vdev);
100680270a19SKONRAD Frederic     DeviceState *proxy = DEVICE(ccw_dev);
10075e5ced38SMarkus Armbruster     Error *err = NULL;
100880270a19SKONRAD Frederic     char *bus_name;
100980270a19SKONRAD Frederic 
101080270a19SKONRAD Frederic     /*
101180270a19SKONRAD Frederic      * For command line compatibility, this sets the virtio-serial-device bus
101280270a19SKONRAD Frederic      * name as before.
101380270a19SKONRAD Frederic      */
101480270a19SKONRAD Frederic     if (proxy->id) {
101580270a19SKONRAD Frederic         bus_name = g_strdup_printf("%s.0", proxy->id);
101680270a19SKONRAD Frederic         virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name);
101780270a19SKONRAD Frederic         g_free(bus_name);
101880270a19SKONRAD Frederic     }
1019a5cf2bb4SCornelia Huck 
10206acf69cdSKONRAD Frederic     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
10215e5ced38SMarkus Armbruster     object_property_set_bool(OBJECT(vdev), true, "realized", &err);
10225e5ced38SMarkus Armbruster     if (err) {
10235e5ced38SMarkus Armbruster         error_propagate(errp, err);
1024a5cf2bb4SCornelia Huck     }
1025a5cf2bb4SCornelia Huck }
1026a5cf2bb4SCornelia Huck 
10276acf69cdSKONRAD Frederic 
10286acf69cdSKONRAD Frederic static void virtio_ccw_serial_instance_init(Object *obj)
1029a5cf2bb4SCornelia Huck {
10306acf69cdSKONRAD Frederic     VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(obj);
1031c8075cafSGonglei 
1032c8075cafSGonglei     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
1033c8075cafSGonglei                                 TYPE_VIRTIO_SERIAL);
1034a5cf2bb4SCornelia Huck }
1035a5cf2bb4SCornelia Huck 
10365e5ced38SMarkus Armbruster static void virtio_ccw_balloon_realize(VirtioCcwDevice *ccw_dev, Error **errp)
1037a5cf2bb4SCornelia Huck {
103830bff6a0SKONRAD Frederic     VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(ccw_dev);
103930bff6a0SKONRAD Frederic     DeviceState *vdev = DEVICE(&dev->vdev);
10405e5ced38SMarkus Armbruster     Error *err = NULL;
1041a5cf2bb4SCornelia Huck 
104230bff6a0SKONRAD Frederic     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
10435e5ced38SMarkus Armbruster     object_property_set_bool(OBJECT(vdev), true, "realized", &err);
10445e5ced38SMarkus Armbruster     if (err) {
10455e5ced38SMarkus Armbruster         error_propagate(errp, err);
1046a5cf2bb4SCornelia Huck     }
1047a5cf2bb4SCornelia Huck }
1048a5cf2bb4SCornelia Huck 
104930bff6a0SKONRAD Frederic static void virtio_ccw_balloon_instance_init(Object *obj)
1050a5cf2bb4SCornelia Huck {
105130bff6a0SKONRAD Frederic     VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(obj);
1052ecfa60e3SShannon Zhao 
1053a6027b0fSDenis V. Lunev     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
1054a6027b0fSDenis V. Lunev                                 TYPE_VIRTIO_BALLOON);
1055ecfa60e3SShannon Zhao     object_property_add_alias(obj, "guest-stats", OBJECT(&dev->vdev),
1056ecfa60e3SShannon Zhao                               "guest-stats", &error_abort);
1057ecfa60e3SShannon Zhao     object_property_add_alias(obj, "guest-stats-polling-interval",
1058ecfa60e3SShannon Zhao                               OBJECT(&dev->vdev),
1059ecfa60e3SShannon Zhao                               "guest-stats-polling-interval", &error_abort);
1060a5cf2bb4SCornelia Huck }
1061a5cf2bb4SCornelia Huck 
10625e5ced38SMarkus Armbruster static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp)
1063a5cf2bb4SCornelia Huck {
1064c908ea10SKONRAD Frederic     VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(ccw_dev);
1065c908ea10SKONRAD Frederic     DeviceState *vdev = DEVICE(&dev->vdev);
10666f32a6b4SKONRAD Frederic     DeviceState *qdev = DEVICE(ccw_dev);
10675e5ced38SMarkus Armbruster     Error *err = NULL;
10686f32a6b4SKONRAD Frederic     char *bus_name;
10696f32a6b4SKONRAD Frederic 
10706f32a6b4SKONRAD Frederic     /*
10716f32a6b4SKONRAD Frederic      * For command line compatibility, this sets the virtio-scsi-device bus
10726f32a6b4SKONRAD Frederic      * name as before.
10736f32a6b4SKONRAD Frederic      */
10746f32a6b4SKONRAD Frederic     if (qdev->id) {
10756f32a6b4SKONRAD Frederic         bus_name = g_strdup_printf("%s.0", qdev->id);
10766f32a6b4SKONRAD Frederic         virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name);
10776f32a6b4SKONRAD Frederic         g_free(bus_name);
10786f32a6b4SKONRAD Frederic     }
1079a5cf2bb4SCornelia Huck 
1080c908ea10SKONRAD Frederic     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
10815e5ced38SMarkus Armbruster     object_property_set_bool(OBJECT(vdev), true, "realized", &err);
10825e5ced38SMarkus Armbruster     if (err) {
10835e5ced38SMarkus Armbruster         error_propagate(errp, err);
1084a5cf2bb4SCornelia Huck     }
1085a5cf2bb4SCornelia Huck }
1086a5cf2bb4SCornelia Huck 
1087c908ea10SKONRAD Frederic static void virtio_ccw_scsi_instance_init(Object *obj)
1088a5cf2bb4SCornelia Huck {
1089c908ea10SKONRAD Frederic     VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(obj);
1090c8075cafSGonglei 
1091c8075cafSGonglei     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
1092c8075cafSGonglei                                 TYPE_VIRTIO_SCSI);
109319d339f1SFam Zheng     object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev), "iothread",
109419d339f1SFam Zheng                               &error_abort);
1095a5cf2bb4SCornelia Huck }
1096a5cf2bb4SCornelia Huck 
1097ccf6916cSPaolo Bonzini #ifdef CONFIG_VHOST_SCSI
10985e5ced38SMarkus Armbruster static void vhost_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp)
1099ccf6916cSPaolo Bonzini {
1100ccf6916cSPaolo Bonzini     VHostSCSICcw *dev = VHOST_SCSI_CCW(ccw_dev);
1101ccf6916cSPaolo Bonzini     DeviceState *vdev = DEVICE(&dev->vdev);
11025e5ced38SMarkus Armbruster     Error *err = NULL;
1103ccf6916cSPaolo Bonzini 
1104ccf6916cSPaolo Bonzini     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
11055e5ced38SMarkus Armbruster     object_property_set_bool(OBJECT(vdev), true, "realized", &err);
11065e5ced38SMarkus Armbruster     if (err) {
11075e5ced38SMarkus Armbruster         error_propagate(errp, err);
1108ccf6916cSPaolo Bonzini     }
1109ccf6916cSPaolo Bonzini }
1110ccf6916cSPaolo Bonzini 
1111ccf6916cSPaolo Bonzini static void vhost_ccw_scsi_instance_init(Object *obj)
1112ccf6916cSPaolo Bonzini {
1113ccf6916cSPaolo Bonzini     VHostSCSICcw *dev = VHOST_SCSI_CCW(obj);
1114c8075cafSGonglei 
1115c8075cafSGonglei     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
1116c8075cafSGonglei                                 TYPE_VHOST_SCSI);
1117ccf6916cSPaolo Bonzini }
1118ccf6916cSPaolo Bonzini #endif
1119ccf6916cSPaolo Bonzini 
11205e5ced38SMarkus Armbruster static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp)
11212362ecc5SCornelia Huck {
11222db26d4cSKONRAD Frederic     VirtIORNGCcw *dev = VIRTIO_RNG_CCW(ccw_dev);
11232db26d4cSKONRAD Frederic     DeviceState *vdev = DEVICE(&dev->vdev);
11245e5ced38SMarkus Armbruster     Error *err = NULL;
11252362ecc5SCornelia Huck 
11262db26d4cSKONRAD Frederic     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
11275e5ced38SMarkus Armbruster     object_property_set_bool(OBJECT(vdev), true, "realized", &err);
11285e5ced38SMarkus Armbruster     if (err) {
11295e5ced38SMarkus Armbruster         error_propagate(errp, err);
11305e5ced38SMarkus Armbruster         return;
11312362ecc5SCornelia Huck     }
11322362ecc5SCornelia Huck 
11332db26d4cSKONRAD Frederic     object_property_set_link(OBJECT(dev),
11345b456438SCole Robinson                              OBJECT(dev->vdev.conf.rng), "rng",
11352db26d4cSKONRAD Frederic                              NULL);
11362362ecc5SCornelia Huck }
11372362ecc5SCornelia Huck 
1138a5cf2bb4SCornelia Huck /* DeviceState to VirtioCcwDevice. Note: used on datapath,
1139a5cf2bb4SCornelia Huck  * be careful and test performance if you change this.
1140a5cf2bb4SCornelia Huck  */
1141a5cf2bb4SCornelia Huck static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d)
1142a5cf2bb4SCornelia Huck {
1143a5cf2bb4SCornelia Huck     return container_of(d, VirtioCcwDevice, parent_obj);
1144a5cf2bb4SCornelia Huck }
1145a5cf2bb4SCornelia Huck 
11467e749462SCornelia Huck static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc,
11477e749462SCornelia Huck                                      uint8_t to_be_set)
11487e749462SCornelia Huck {
11497e749462SCornelia Huck     uint8_t ind_old, ind_new;
11507e749462SCornelia Huck     hwaddr len = 1;
11517e749462SCornelia Huck     uint8_t *ind_addr;
11527e749462SCornelia Huck 
11537e749462SCornelia Huck     ind_addr = cpu_physical_memory_map(ind_loc, &len, 1);
11547e749462SCornelia Huck     if (!ind_addr) {
11557e749462SCornelia Huck         error_report("%s(%x.%x.%04x): unable to access indicator",
11567e749462SCornelia Huck                      __func__, sch->cssid, sch->ssid, sch->schid);
11577e749462SCornelia Huck         return -1;
11587e749462SCornelia Huck     }
11597e749462SCornelia Huck     do {
11607e749462SCornelia Huck         ind_old = *ind_addr;
11617e749462SCornelia Huck         ind_new = ind_old | to_be_set;
11627e749462SCornelia Huck     } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old);
11637e749462SCornelia Huck     cpu_physical_memory_unmap(ind_addr, len, 1, len);
11647e749462SCornelia Huck 
11657e749462SCornelia Huck     return ind_old;
11667e749462SCornelia Huck }
11677e749462SCornelia Huck 
1168a5cf2bb4SCornelia Huck static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
1169a5cf2bb4SCornelia Huck {
1170a5cf2bb4SCornelia Huck     VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d);
1171a5cf2bb4SCornelia Huck     SubchDev *sch = dev->sch;
1172a5cf2bb4SCornelia Huck     uint64_t indicators;
1173a5cf2bb4SCornelia Huck 
1174a5cf2bb4SCornelia Huck     if (vector >= 128) {
1175a5cf2bb4SCornelia Huck         return;
1176a5cf2bb4SCornelia Huck     }
1177a5cf2bb4SCornelia Huck 
11788dfbaa6aSJason Wang     if (vector < VIRTIO_CCW_QUEUE_MAX) {
11797c486976SCornelia Huck         if (!dev->indicators) {
11807c486976SCornelia Huck             return;
11817c486976SCornelia Huck         }
11827e749462SCornelia Huck         if (sch->thinint_active) {
11837e749462SCornelia Huck             /*
11847e749462SCornelia Huck              * In the adapter interrupt case, indicators points to a
11857e749462SCornelia Huck              * memory area that may be (way) larger than 64 bit and
11867e749462SCornelia Huck              * ind_bit indicates the start of the indicators in a big
11877e749462SCornelia Huck              * endian notation.
11887e749462SCornelia Huck              */
1189d426d9fbSCornelia Huck             uint64_t ind_bit = dev->routes.adapter.ind_offset;
1190d426d9fbSCornelia Huck 
11917bca3892SCornelia Huck             virtio_set_ind_atomic(sch, dev->indicators->addr +
1192d426d9fbSCornelia Huck                                   (ind_bit + vector) / 8,
1193d426d9fbSCornelia Huck                                   0x80 >> ((ind_bit + vector) % 8));
11947bca3892SCornelia Huck             if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr,
11957e749462SCornelia Huck                                        0x01)) {
11967e749462SCornelia Huck                 css_adapter_interrupt(dev->thinint_isc);
11977e749462SCornelia Huck             }
11987e749462SCornelia Huck         } else {
119942874d3aSPeter Maydell             indicators = address_space_ldq(&address_space_memory,
120042874d3aSPeter Maydell                                            dev->indicators->addr,
120142874d3aSPeter Maydell                                            MEMTXATTRS_UNSPECIFIED,
120242874d3aSPeter Maydell                                            NULL);
120319380b1bSCornelia Huck             indicators |= 1ULL << vector;
120442874d3aSPeter Maydell             address_space_stq(&address_space_memory, dev->indicators->addr,
120542874d3aSPeter Maydell                               indicators, MEMTXATTRS_UNSPECIFIED, NULL);
12067e749462SCornelia Huck             css_conditional_io_interrupt(sch);
12077e749462SCornelia Huck         }
1208a5cf2bb4SCornelia Huck     } else {
12097c486976SCornelia Huck         if (!dev->indicators2) {
12107c486976SCornelia Huck             return;
12117c486976SCornelia Huck         }
1212a5cf2bb4SCornelia Huck         vector = 0;
121342874d3aSPeter Maydell         indicators = address_space_ldq(&address_space_memory,
121442874d3aSPeter Maydell                                        dev->indicators2->addr,
121542874d3aSPeter Maydell                                        MEMTXATTRS_UNSPECIFIED,
121642874d3aSPeter Maydell                                        NULL);
121719380b1bSCornelia Huck         indicators |= 1ULL << vector;
121842874d3aSPeter Maydell         address_space_stq(&address_space_memory, dev->indicators2->addr,
121942874d3aSPeter Maydell                           indicators, MEMTXATTRS_UNSPECIFIED, NULL);
1220a5cf2bb4SCornelia Huck         css_conditional_io_interrupt(sch);
12217e749462SCornelia Huck     }
1222a5cf2bb4SCornelia Huck }
1223a5cf2bb4SCornelia Huck 
1224a5cf2bb4SCornelia Huck static void virtio_ccw_reset(DeviceState *d)
1225a5cf2bb4SCornelia Huck {
1226a5cf2bb4SCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1227f24a6840SPaolo Bonzini     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1228a5cf2bb4SCornelia Huck 
1229fa8b0ca5SCornelia Huck     virtio_ccw_reset_virtio(dev, vdev);
1230a5cf2bb4SCornelia Huck     css_reset_sch(dev->sch);
1231a5cf2bb4SCornelia Huck }
1232a5cf2bb4SCornelia Huck 
1233b4436a0bSCornelia Huck static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
1234b4436a0bSCornelia Huck {
1235b4436a0bSCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1236b4436a0bSCornelia Huck 
1237b4436a0bSCornelia Huck     if (running) {
1238b4436a0bSCornelia Huck         virtio_ccw_start_ioeventfd(dev);
1239b4436a0bSCornelia Huck     } else {
1240b4436a0bSCornelia Huck         virtio_ccw_stop_ioeventfd(dev);
1241b4436a0bSCornelia Huck     }
1242b4436a0bSCornelia Huck }
1243b4436a0bSCornelia Huck 
1244320ce850SCornelia Huck static bool virtio_ccw_query_guest_notifiers(DeviceState *d)
1245320ce850SCornelia Huck {
1246320ce850SCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1247320ce850SCornelia Huck 
1248320ce850SCornelia Huck     return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA);
1249320ce850SCornelia Huck }
1250320ce850SCornelia Huck 
1251320ce850SCornelia Huck static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign)
1252320ce850SCornelia Huck {
1253320ce850SCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1254320ce850SCornelia Huck 
1255320ce850SCornelia Huck     /* Stop using the generic ioeventfd, we are doing eventfd handling
1256320ce850SCornelia Huck      * ourselves below */
1257320ce850SCornelia Huck     dev->ioeventfd_disabled = assign;
1258320ce850SCornelia Huck     if (assign) {
1259320ce850SCornelia Huck         virtio_ccw_stop_ioeventfd(dev);
1260320ce850SCornelia Huck     }
1261320ce850SCornelia Huck     return virtio_ccw_set_guest2host_notifier(dev, n, assign, false);
1262320ce850SCornelia Huck }
1263320ce850SCornelia Huck 
1264d426d9fbSCornelia Huck static int virtio_ccw_get_mappings(VirtioCcwDevice *dev)
1265d426d9fbSCornelia Huck {
1266d426d9fbSCornelia Huck     int r;
1267d426d9fbSCornelia Huck 
1268d426d9fbSCornelia Huck     if (!dev->sch->thinint_active) {
1269d426d9fbSCornelia Huck         return -EINVAL;
1270d426d9fbSCornelia Huck     }
1271d426d9fbSCornelia Huck 
1272d426d9fbSCornelia Huck     r = map_indicator(&dev->routes.adapter, dev->summary_indicator);
1273d426d9fbSCornelia Huck     if (r) {
1274d426d9fbSCornelia Huck         return r;
1275d426d9fbSCornelia Huck     }
1276d426d9fbSCornelia Huck     r = map_indicator(&dev->routes.adapter, dev->indicators);
1277d426d9fbSCornelia Huck     if (r) {
1278d426d9fbSCornelia Huck         return r;
1279d426d9fbSCornelia Huck     }
1280d426d9fbSCornelia Huck     dev->routes.adapter.summary_addr = dev->summary_indicator->map;
1281d426d9fbSCornelia Huck     dev->routes.adapter.ind_addr = dev->indicators->map;
1282d426d9fbSCornelia Huck 
1283d426d9fbSCornelia Huck     return 0;
1284d426d9fbSCornelia Huck }
1285d426d9fbSCornelia Huck 
1286d426d9fbSCornelia Huck static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs)
1287d426d9fbSCornelia Huck {
1288d426d9fbSCornelia Huck     int i;
1289d426d9fbSCornelia Huck     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1290d426d9fbSCornelia Huck     int ret;
1291d426d9fbSCornelia Huck     S390FLICState *fs = s390_get_flic();
1292d426d9fbSCornelia Huck     S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
1293d426d9fbSCornelia Huck 
1294d426d9fbSCornelia Huck     ret = virtio_ccw_get_mappings(dev);
1295d426d9fbSCornelia Huck     if (ret) {
1296d426d9fbSCornelia Huck         return ret;
1297d426d9fbSCornelia Huck     }
1298d426d9fbSCornelia Huck     for (i = 0; i < nvqs; i++) {
1299d426d9fbSCornelia Huck         if (!virtio_queue_get_num(vdev, i)) {
1300d426d9fbSCornelia Huck             break;
1301d426d9fbSCornelia Huck         }
1302d426d9fbSCornelia Huck     }
1303d426d9fbSCornelia Huck     dev->routes.num_routes = i;
1304d426d9fbSCornelia Huck     return fsc->add_adapter_routes(fs, &dev->routes);
1305d426d9fbSCornelia Huck }
1306d426d9fbSCornelia Huck 
1307d426d9fbSCornelia Huck static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs)
1308d426d9fbSCornelia Huck {
1309d426d9fbSCornelia Huck     S390FLICState *fs = s390_get_flic();
1310d426d9fbSCornelia Huck     S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
1311d426d9fbSCornelia Huck 
1312d426d9fbSCornelia Huck     fsc->release_adapter_routes(fs, &dev->routes);
1313d426d9fbSCornelia Huck }
1314d426d9fbSCornelia Huck 
1315d426d9fbSCornelia Huck static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n)
1316d426d9fbSCornelia Huck {
1317d426d9fbSCornelia Huck     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1318d426d9fbSCornelia Huck     VirtQueue *vq = virtio_get_queue(vdev, n);
1319d426d9fbSCornelia Huck     EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
1320d426d9fbSCornelia Huck 
13211c9b71a7SEric Auger     return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, notifier, NULL,
1322d426d9fbSCornelia Huck                                               dev->routes.gsi[n]);
1323d426d9fbSCornelia Huck }
1324d426d9fbSCornelia Huck 
1325d426d9fbSCornelia Huck static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n)
1326d426d9fbSCornelia Huck {
1327d426d9fbSCornelia Huck     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1328d426d9fbSCornelia Huck     VirtQueue *vq = virtio_get_queue(vdev, n);
1329d426d9fbSCornelia Huck     EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
1330d426d9fbSCornelia Huck     int ret;
1331d426d9fbSCornelia Huck 
13321c9b71a7SEric Auger     ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, notifier,
1333d426d9fbSCornelia Huck                                                 dev->routes.gsi[n]);
1334d426d9fbSCornelia Huck     assert(ret == 0);
1335d426d9fbSCornelia Huck }
1336d426d9fbSCornelia Huck 
1337320ce850SCornelia Huck static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
1338320ce850SCornelia Huck                                          bool assign, bool with_irqfd)
1339320ce850SCornelia Huck {
1340f24a6840SPaolo Bonzini     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1341f24a6840SPaolo Bonzini     VirtQueue *vq = virtio_get_queue(vdev, n);
1342320ce850SCornelia Huck     EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
1343f24a6840SPaolo Bonzini     VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
1344320ce850SCornelia Huck 
1345320ce850SCornelia Huck     if (assign) {
1346320ce850SCornelia Huck         int r = event_notifier_init(notifier, 0);
1347320ce850SCornelia Huck 
1348320ce850SCornelia Huck         if (r < 0) {
1349320ce850SCornelia Huck             return r;
1350320ce850SCornelia Huck         }
1351320ce850SCornelia Huck         virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd);
1352d426d9fbSCornelia Huck         if (with_irqfd) {
1353d426d9fbSCornelia Huck             r = virtio_ccw_add_irqfd(dev, n);
1354d426d9fbSCornelia Huck             if (r) {
1355d426d9fbSCornelia Huck                 virtio_queue_set_guest_notifier_fd_handler(vq, false,
1356d426d9fbSCornelia Huck                                                            with_irqfd);
1357d426d9fbSCornelia Huck                 return r;
1358d426d9fbSCornelia Huck             }
1359d426d9fbSCornelia Huck         }
1360d426d9fbSCornelia Huck         /*
1361d426d9fbSCornelia Huck          * We do not support individual masking for channel devices, so we
1362d426d9fbSCornelia Huck          * need to manually trigger any guest masking callbacks here.
1363320ce850SCornelia Huck          */
1364320ce850SCornelia Huck         if (k->guest_notifier_mask) {
1365f24a6840SPaolo Bonzini             k->guest_notifier_mask(vdev, n, false);
1366320ce850SCornelia Huck         }
1367320ce850SCornelia Huck         /* get lost events and re-inject */
1368320ce850SCornelia Huck         if (k->guest_notifier_pending &&
1369f24a6840SPaolo Bonzini             k->guest_notifier_pending(vdev, n)) {
1370320ce850SCornelia Huck             event_notifier_set(notifier);
1371320ce850SCornelia Huck         }
1372320ce850SCornelia Huck     } else {
1373320ce850SCornelia Huck         if (k->guest_notifier_mask) {
1374f24a6840SPaolo Bonzini             k->guest_notifier_mask(vdev, n, true);
1375320ce850SCornelia Huck         }
1376d426d9fbSCornelia Huck         if (with_irqfd) {
1377d426d9fbSCornelia Huck             virtio_ccw_remove_irqfd(dev, n);
1378d426d9fbSCornelia Huck         }
1379320ce850SCornelia Huck         virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
1380320ce850SCornelia Huck         event_notifier_cleanup(notifier);
1381320ce850SCornelia Huck     }
1382320ce850SCornelia Huck     return 0;
1383320ce850SCornelia Huck }
1384320ce850SCornelia Huck 
1385320ce850SCornelia Huck static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
1386320ce850SCornelia Huck                                           bool assigned)
1387320ce850SCornelia Huck {
1388320ce850SCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1389f24a6840SPaolo Bonzini     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1390d426d9fbSCornelia Huck     bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled();
1391320ce850SCornelia Huck     int r, n;
1392320ce850SCornelia Huck 
1393d426d9fbSCornelia Huck     if (with_irqfd && assigned) {
1394d426d9fbSCornelia Huck         /* irq routes need to be set up before assigning irqfds */
1395d426d9fbSCornelia Huck         r = virtio_ccw_setup_irqroutes(dev, nvqs);
1396d426d9fbSCornelia Huck         if (r < 0) {
1397d426d9fbSCornelia Huck             goto irqroute_error;
1398d426d9fbSCornelia Huck         }
1399d426d9fbSCornelia Huck     }
1400320ce850SCornelia Huck     for (n = 0; n < nvqs; n++) {
1401320ce850SCornelia Huck         if (!virtio_queue_get_num(vdev, n)) {
1402320ce850SCornelia Huck             break;
1403320ce850SCornelia Huck         }
1404d426d9fbSCornelia Huck         r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd);
1405320ce850SCornelia Huck         if (r < 0) {
1406320ce850SCornelia Huck             goto assign_error;
1407320ce850SCornelia Huck         }
1408320ce850SCornelia Huck     }
1409d426d9fbSCornelia Huck     if (with_irqfd && !assigned) {
1410d426d9fbSCornelia Huck         /* release irq routes after irqfds have been released */
1411d426d9fbSCornelia Huck         virtio_ccw_release_irqroutes(dev, nvqs);
1412d426d9fbSCornelia Huck     }
1413320ce850SCornelia Huck     return 0;
1414320ce850SCornelia Huck 
1415320ce850SCornelia Huck assign_error:
1416320ce850SCornelia Huck     while (--n >= 0) {
1417320ce850SCornelia Huck         virtio_ccw_set_guest_notifier(dev, n, !assigned, false);
1418320ce850SCornelia Huck     }
1419d426d9fbSCornelia Huck irqroute_error:
1420d426d9fbSCornelia Huck     if (with_irqfd && assigned) {
1421d426d9fbSCornelia Huck         virtio_ccw_release_irqroutes(dev, nvqs);
1422d426d9fbSCornelia Huck     }
1423320ce850SCornelia Huck     return r;
1424320ce850SCornelia Huck }
1425320ce850SCornelia Huck 
1426bcb2b582SJens Freimann static void virtio_ccw_save_queue(DeviceState *d, int n, QEMUFile *f)
1427bcb2b582SJens Freimann {
1428bcb2b582SJens Freimann     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1429bcb2b582SJens Freimann     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1430bcb2b582SJens Freimann 
1431bcb2b582SJens Freimann     qemu_put_be16(f, virtio_queue_vector(vdev, n));
1432bcb2b582SJens Freimann }
1433bcb2b582SJens Freimann 
1434bcb2b582SJens Freimann static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f)
1435bcb2b582SJens Freimann {
1436bcb2b582SJens Freimann     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1437bcb2b582SJens Freimann     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1438bcb2b582SJens Freimann     uint16_t vector;
1439bcb2b582SJens Freimann 
1440bcb2b582SJens Freimann     qemu_get_be16s(f, &vector);
1441bcb2b582SJens Freimann     virtio_queue_set_vector(vdev, n , vector);
1442bcb2b582SJens Freimann 
1443bcb2b582SJens Freimann     return 0;
1444bcb2b582SJens Freimann }
1445bcb2b582SJens Freimann 
1446bcb2b582SJens Freimann static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
1447bcb2b582SJens Freimann {
1448bcb2b582SJens Freimann     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1449bcb2b582SJens Freimann     SubchDev *s = dev->sch;
14502a72ea5fSJason J. Herne     VirtIODevice *vdev = virtio_ccw_get_vdev(s);
1451bcb2b582SJens Freimann 
1452bcb2b582SJens Freimann     subch_device_save(s, f);
1453bcb2b582SJens Freimann     if (dev->indicators != NULL) {
1454bcb2b582SJens Freimann         qemu_put_be32(f, dev->indicators->len);
1455bcb2b582SJens Freimann         qemu_put_be64(f, dev->indicators->addr);
1456bcb2b582SJens Freimann     } else {
1457bcb2b582SJens Freimann         qemu_put_be32(f, 0);
1458bcb2b582SJens Freimann         qemu_put_be64(f, 0UL);
1459bcb2b582SJens Freimann     }
1460bcb2b582SJens Freimann     if (dev->indicators2 != NULL) {
1461bcb2b582SJens Freimann         qemu_put_be32(f, dev->indicators2->len);
1462bcb2b582SJens Freimann         qemu_put_be64(f, dev->indicators2->addr);
1463bcb2b582SJens Freimann     } else {
1464bcb2b582SJens Freimann         qemu_put_be32(f, 0);
1465bcb2b582SJens Freimann         qemu_put_be64(f, 0UL);
1466bcb2b582SJens Freimann     }
1467bcb2b582SJens Freimann     if (dev->summary_indicator != NULL) {
1468bcb2b582SJens Freimann         qemu_put_be32(f, dev->summary_indicator->len);
1469bcb2b582SJens Freimann         qemu_put_be64(f, dev->summary_indicator->addr);
1470bcb2b582SJens Freimann     } else {
1471bcb2b582SJens Freimann         qemu_put_be32(f, 0);
1472bcb2b582SJens Freimann         qemu_put_be64(f, 0UL);
1473bcb2b582SJens Freimann     }
14742a72ea5fSJason J. Herne     qemu_put_be16(f, vdev->config_vector);
1475bcb2b582SJens Freimann     qemu_put_be64(f, dev->routes.adapter.ind_offset);
1476bcb2b582SJens Freimann     qemu_put_byte(f, dev->thinint_isc);
1477213941d7SCornelia Huck     qemu_put_be32(f, dev->revision);
1478bcb2b582SJens Freimann }
1479bcb2b582SJens Freimann 
1480bcb2b582SJens Freimann static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
1481bcb2b582SJens Freimann {
1482bcb2b582SJens Freimann     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1483bcb2b582SJens Freimann     SubchDev *s = dev->sch;
14842a72ea5fSJason J. Herne     VirtIODevice *vdev = virtio_ccw_get_vdev(s);
1485bcb2b582SJens Freimann     int len;
1486bcb2b582SJens Freimann 
1487bcb2b582SJens Freimann     s->driver_data = dev;
1488bcb2b582SJens Freimann     subch_device_load(s, f);
1489bcb2b582SJens Freimann     len = qemu_get_be32(f);
1490bcb2b582SJens Freimann     if (len != 0) {
1491bcb2b582SJens Freimann         dev->indicators = get_indicator(qemu_get_be64(f), len);
1492bcb2b582SJens Freimann     } else {
1493bcb2b582SJens Freimann         qemu_get_be64(f);
1494bcb2b582SJens Freimann         dev->indicators = NULL;
1495bcb2b582SJens Freimann     }
1496bcb2b582SJens Freimann     len = qemu_get_be32(f);
1497bcb2b582SJens Freimann     if (len != 0) {
1498bcb2b582SJens Freimann         dev->indicators2 = get_indicator(qemu_get_be64(f), len);
1499bcb2b582SJens Freimann     } else {
1500bcb2b582SJens Freimann         qemu_get_be64(f);
1501bcb2b582SJens Freimann         dev->indicators2 = NULL;
1502bcb2b582SJens Freimann     }
1503bcb2b582SJens Freimann     len = qemu_get_be32(f);
1504bcb2b582SJens Freimann     if (len != 0) {
1505bcb2b582SJens Freimann         dev->summary_indicator = get_indicator(qemu_get_be64(f), len);
1506bcb2b582SJens Freimann     } else {
1507bcb2b582SJens Freimann         qemu_get_be64(f);
1508bcb2b582SJens Freimann         dev->summary_indicator = NULL;
1509bcb2b582SJens Freimann     }
15102a72ea5fSJason J. Herne     qemu_get_be16s(f, &vdev->config_vector);
1511bcb2b582SJens Freimann     dev->routes.adapter.ind_offset = qemu_get_be64(f);
1512bcb2b582SJens Freimann     dev->thinint_isc = qemu_get_byte(f);
15132af9170cSChristian Borntraeger     dev->revision = qemu_get_be32(f);
1514bcb2b582SJens Freimann     if (s->thinint_active) {
1515bcb2b582SJens Freimann         return css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO,
1516bcb2b582SJens Freimann                                        dev->thinint_isc, true, false,
1517bcb2b582SJens Freimann                                        &dev->routes.adapter.adapter_id);
1518bcb2b582SJens Freimann     }
1519bcb2b582SJens Freimann 
1520bcb2b582SJens Freimann     return 0;
1521bcb2b582SJens Freimann }
1522bcb2b582SJens Freimann 
1523fb846a09SCornelia Huck /* This is called by virtio-bus just after the device is plugged. */
1524e8398045SJason Wang static void virtio_ccw_device_plugged(DeviceState *d, Error **errp)
1525fb846a09SCornelia Huck {
1526fb846a09SCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
152710ceaa1eSJason Wang     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
1528fb846a09SCornelia Huck     SubchDev *sch = dev->sch;
152910ceaa1eSJason Wang     int n = virtio_get_num_queues(vdev);
153010ceaa1eSJason Wang 
153110ceaa1eSJason Wang     if (virtio_get_num_queues(vdev) > VIRTIO_CCW_QUEUE_MAX) {
153210ceaa1eSJason Wang         error_setg(errp, "The nubmer of virtqueues %d "
153310ceaa1eSJason Wang                    "exceeds ccw limit %d", n,
153410ceaa1eSJason Wang                    VIRTIO_CCW_QUEUE_MAX);
153510ceaa1eSJason Wang         return;
153610ceaa1eSJason Wang     }
1537fb846a09SCornelia Huck 
1538a499973fSAurelien Jarno     if (!kvm_eventfds_enabled()) {
1539a499973fSAurelien Jarno         dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD;
1540a499973fSAurelien Jarno     }
1541a499973fSAurelien Jarno 
1542fb846a09SCornelia Huck     sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus);
1543fb846a09SCornelia Huck 
1544fb846a09SCornelia Huck     css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid,
1545fb846a09SCornelia Huck                           d->hotplugged, 1);
1546fb846a09SCornelia Huck }
1547fb846a09SCornelia Huck 
1548fb846a09SCornelia Huck static void virtio_ccw_device_unplugged(DeviceState *d)
1549fb846a09SCornelia Huck {
1550fb846a09SCornelia Huck     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
1551fb846a09SCornelia Huck 
1552fb846a09SCornelia Huck     virtio_ccw_stop_ioeventfd(dev);
1553fb846a09SCornelia Huck }
1554a5cf2bb4SCornelia Huck /**************** Virtio-ccw Bus Device Descriptions *******************/
1555a5cf2bb4SCornelia Huck 
1556a5cf2bb4SCornelia Huck static Property virtio_ccw_net_properties[] = {
1557a5cf2bb4SCornelia Huck     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
1558b4436a0bSCornelia Huck     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
1559b4436a0bSCornelia Huck                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
1560a5cf2bb4SCornelia Huck     DEFINE_PROP_END_OF_LIST(),
1561a5cf2bb4SCornelia Huck };
1562a5cf2bb4SCornelia Huck 
1563a5cf2bb4SCornelia Huck static void virtio_ccw_net_class_init(ObjectClass *klass, void *data)
1564a5cf2bb4SCornelia Huck {
1565a5cf2bb4SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(klass);
1566a5cf2bb4SCornelia Huck     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
1567a5cf2bb4SCornelia Huck 
15685e5ced38SMarkus Armbruster     k->realize = virtio_ccw_net_realize;
156989334c8bSKONRAD Frederic     k->exit = virtio_ccw_exit;
1570a5cf2bb4SCornelia Huck     dc->reset = virtio_ccw_reset;
1571a5cf2bb4SCornelia Huck     dc->props = virtio_ccw_net_properties;
1572cd20d616SCornelia Huck     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
1573a5cf2bb4SCornelia Huck }
1574a5cf2bb4SCornelia Huck 
1575a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_net = {
157689334c8bSKONRAD Frederic     .name          = TYPE_VIRTIO_NET_CCW,
1577a5cf2bb4SCornelia Huck     .parent        = TYPE_VIRTIO_CCW_DEVICE,
157889334c8bSKONRAD Frederic     .instance_size = sizeof(VirtIONetCcw),
157989334c8bSKONRAD Frederic     .instance_init = virtio_ccw_net_instance_init,
1580a5cf2bb4SCornelia Huck     .class_init    = virtio_ccw_net_class_init,
1581a5cf2bb4SCornelia Huck };
1582a5cf2bb4SCornelia Huck 
1583a5cf2bb4SCornelia Huck static Property virtio_ccw_blk_properties[] = {
1584a5cf2bb4SCornelia Huck     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
1585b4436a0bSCornelia Huck     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
1586b4436a0bSCornelia Huck                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
1587a5cf2bb4SCornelia Huck     DEFINE_PROP_END_OF_LIST(),
1588a5cf2bb4SCornelia Huck };
1589a5cf2bb4SCornelia Huck 
1590a5cf2bb4SCornelia Huck static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data)
1591a5cf2bb4SCornelia Huck {
1592a5cf2bb4SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(klass);
1593a5cf2bb4SCornelia Huck     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
1594a5cf2bb4SCornelia Huck 
15955e5ced38SMarkus Armbruster     k->realize = virtio_ccw_blk_realize;
15963400c455SKONRAD Frederic     k->exit = virtio_ccw_exit;
1597a5cf2bb4SCornelia Huck     dc->reset = virtio_ccw_reset;
1598a5cf2bb4SCornelia Huck     dc->props = virtio_ccw_blk_properties;
1599cd20d616SCornelia Huck     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
1600a5cf2bb4SCornelia Huck }
1601a5cf2bb4SCornelia Huck 
1602a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_blk = {
16033400c455SKONRAD Frederic     .name          = TYPE_VIRTIO_BLK_CCW,
1604a5cf2bb4SCornelia Huck     .parent        = TYPE_VIRTIO_CCW_DEVICE,
16053400c455SKONRAD Frederic     .instance_size = sizeof(VirtIOBlkCcw),
16063400c455SKONRAD Frederic     .instance_init = virtio_ccw_blk_instance_init,
1607a5cf2bb4SCornelia Huck     .class_init    = virtio_ccw_blk_class_init,
1608a5cf2bb4SCornelia Huck };
1609a5cf2bb4SCornelia Huck 
1610a5cf2bb4SCornelia Huck static Property virtio_ccw_serial_properties[] = {
1611a5cf2bb4SCornelia Huck     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
1612b4436a0bSCornelia Huck     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
1613b4436a0bSCornelia Huck                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
1614a5cf2bb4SCornelia Huck     DEFINE_PROP_END_OF_LIST(),
1615a5cf2bb4SCornelia Huck };
1616a5cf2bb4SCornelia Huck 
1617a5cf2bb4SCornelia Huck static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data)
1618a5cf2bb4SCornelia Huck {
1619a5cf2bb4SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(klass);
1620a5cf2bb4SCornelia Huck     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
1621a5cf2bb4SCornelia Huck 
16225e5ced38SMarkus Armbruster     k->realize = virtio_ccw_serial_realize;
16236acf69cdSKONRAD Frederic     k->exit = virtio_ccw_exit;
1624a5cf2bb4SCornelia Huck     dc->reset = virtio_ccw_reset;
1625a5cf2bb4SCornelia Huck     dc->props = virtio_ccw_serial_properties;
1626cd20d616SCornelia Huck     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
1627a5cf2bb4SCornelia Huck }
1628a5cf2bb4SCornelia Huck 
1629a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_serial = {
16306acf69cdSKONRAD Frederic     .name          = TYPE_VIRTIO_SERIAL_CCW,
1631a5cf2bb4SCornelia Huck     .parent        = TYPE_VIRTIO_CCW_DEVICE,
16326acf69cdSKONRAD Frederic     .instance_size = sizeof(VirtioSerialCcw),
16336acf69cdSKONRAD Frederic     .instance_init = virtio_ccw_serial_instance_init,
1634a5cf2bb4SCornelia Huck     .class_init    = virtio_ccw_serial_class_init,
1635a5cf2bb4SCornelia Huck };
1636a5cf2bb4SCornelia Huck 
1637a5cf2bb4SCornelia Huck static Property virtio_ccw_balloon_properties[] = {
1638a5cf2bb4SCornelia Huck     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
1639b4436a0bSCornelia Huck     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
1640b4436a0bSCornelia Huck                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
1641a5cf2bb4SCornelia Huck     DEFINE_PROP_END_OF_LIST(),
1642a5cf2bb4SCornelia Huck };
1643a5cf2bb4SCornelia Huck 
1644a5cf2bb4SCornelia Huck static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data)
1645a5cf2bb4SCornelia Huck {
1646a5cf2bb4SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(klass);
1647a5cf2bb4SCornelia Huck     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
1648a5cf2bb4SCornelia Huck 
16495e5ced38SMarkus Armbruster     k->realize = virtio_ccw_balloon_realize;
165030bff6a0SKONRAD Frederic     k->exit = virtio_ccw_exit;
1651a5cf2bb4SCornelia Huck     dc->reset = virtio_ccw_reset;
1652a5cf2bb4SCornelia Huck     dc->props = virtio_ccw_balloon_properties;
1653cd20d616SCornelia Huck     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
1654a5cf2bb4SCornelia Huck }
1655a5cf2bb4SCornelia Huck 
1656a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_balloon = {
165730bff6a0SKONRAD Frederic     .name          = TYPE_VIRTIO_BALLOON_CCW,
1658a5cf2bb4SCornelia Huck     .parent        = TYPE_VIRTIO_CCW_DEVICE,
165930bff6a0SKONRAD Frederic     .instance_size = sizeof(VirtIOBalloonCcw),
166030bff6a0SKONRAD Frederic     .instance_init = virtio_ccw_balloon_instance_init,
1661a5cf2bb4SCornelia Huck     .class_init    = virtio_ccw_balloon_class_init,
1662a5cf2bb4SCornelia Huck };
1663a5cf2bb4SCornelia Huck 
1664a5cf2bb4SCornelia Huck static Property virtio_ccw_scsi_properties[] = {
1665a5cf2bb4SCornelia Huck     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
1666b4436a0bSCornelia Huck     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
1667b4436a0bSCornelia Huck                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
1668a5cf2bb4SCornelia Huck     DEFINE_PROP_END_OF_LIST(),
1669a5cf2bb4SCornelia Huck };
1670a5cf2bb4SCornelia Huck 
1671a5cf2bb4SCornelia Huck static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data)
1672a5cf2bb4SCornelia Huck {
1673a5cf2bb4SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(klass);
1674a5cf2bb4SCornelia Huck     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
1675a5cf2bb4SCornelia Huck 
16765e5ced38SMarkus Armbruster     k->realize = virtio_ccw_scsi_realize;
1677c908ea10SKONRAD Frederic     k->exit = virtio_ccw_exit;
1678a5cf2bb4SCornelia Huck     dc->reset = virtio_ccw_reset;
1679a5cf2bb4SCornelia Huck     dc->props = virtio_ccw_scsi_properties;
1680cd20d616SCornelia Huck     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
1681a5cf2bb4SCornelia Huck }
1682a5cf2bb4SCornelia Huck 
1683a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_scsi = {
1684c908ea10SKONRAD Frederic     .name          = TYPE_VIRTIO_SCSI_CCW,
1685a5cf2bb4SCornelia Huck     .parent        = TYPE_VIRTIO_CCW_DEVICE,
1686c908ea10SKONRAD Frederic     .instance_size = sizeof(VirtIOSCSICcw),
1687c908ea10SKONRAD Frederic     .instance_init = virtio_ccw_scsi_instance_init,
1688a5cf2bb4SCornelia Huck     .class_init    = virtio_ccw_scsi_class_init,
1689a5cf2bb4SCornelia Huck };
1690a5cf2bb4SCornelia Huck 
1691ccf6916cSPaolo Bonzini #ifdef CONFIG_VHOST_SCSI
1692ccf6916cSPaolo Bonzini static Property vhost_ccw_scsi_properties[] = {
1693ccf6916cSPaolo Bonzini     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
1694ccf6916cSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
1695ccf6916cSPaolo Bonzini };
1696ccf6916cSPaolo Bonzini 
1697ccf6916cSPaolo Bonzini static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data)
1698ccf6916cSPaolo Bonzini {
1699ccf6916cSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
1700ccf6916cSPaolo Bonzini     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
1701ccf6916cSPaolo Bonzini 
17025e5ced38SMarkus Armbruster     k->realize = vhost_ccw_scsi_realize;
1703ccf6916cSPaolo Bonzini     k->exit = virtio_ccw_exit;
1704ccf6916cSPaolo Bonzini     dc->reset = virtio_ccw_reset;
1705ccf6916cSPaolo Bonzini     dc->props = vhost_ccw_scsi_properties;
1706cd20d616SCornelia Huck     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
1707ccf6916cSPaolo Bonzini }
1708ccf6916cSPaolo Bonzini 
1709ccf6916cSPaolo Bonzini static const TypeInfo vhost_ccw_scsi = {
1710ccf6916cSPaolo Bonzini     .name          = TYPE_VHOST_SCSI_CCW,
1711ccf6916cSPaolo Bonzini     .parent        = TYPE_VIRTIO_CCW_DEVICE,
17124b7757baSCornelia Huck     .instance_size = sizeof(VHostSCSICcw),
1713ccf6916cSPaolo Bonzini     .instance_init = vhost_ccw_scsi_instance_init,
1714ccf6916cSPaolo Bonzini     .class_init    = vhost_ccw_scsi_class_init,
1715ccf6916cSPaolo Bonzini };
1716ccf6916cSPaolo Bonzini #endif
1717ccf6916cSPaolo Bonzini 
17182db26d4cSKONRAD Frederic static void virtio_ccw_rng_instance_init(Object *obj)
17192362ecc5SCornelia Huck {
17202db26d4cSKONRAD Frederic     VirtIORNGCcw *dev = VIRTIO_RNG_CCW(obj);
1721c8075cafSGonglei 
1722c8075cafSGonglei     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
1723c8075cafSGonglei                                 TYPE_VIRTIO_RNG);
1724cbd5ac69SPaolo Bonzini     object_property_add_alias(obj, "rng", OBJECT(&dev->vdev),
1725cbd5ac69SPaolo Bonzini                               "rng", &error_abort);
17262362ecc5SCornelia Huck }
17272362ecc5SCornelia Huck 
17282362ecc5SCornelia Huck static Property virtio_ccw_rng_properties[] = {
17292362ecc5SCornelia Huck     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
1730b4436a0bSCornelia Huck     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
1731b4436a0bSCornelia Huck                     VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
17322362ecc5SCornelia Huck     DEFINE_PROP_END_OF_LIST(),
17332362ecc5SCornelia Huck };
17342362ecc5SCornelia Huck 
17352362ecc5SCornelia Huck static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data)
17362362ecc5SCornelia Huck {
17372362ecc5SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(klass);
17382362ecc5SCornelia Huck     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
17392362ecc5SCornelia Huck 
17405e5ced38SMarkus Armbruster     k->realize = virtio_ccw_rng_realize;
17412db26d4cSKONRAD Frederic     k->exit = virtio_ccw_exit;
17422362ecc5SCornelia Huck     dc->reset = virtio_ccw_reset;
17432362ecc5SCornelia Huck     dc->props = virtio_ccw_rng_properties;
1744cd20d616SCornelia Huck     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
17452362ecc5SCornelia Huck }
17462362ecc5SCornelia Huck 
17472362ecc5SCornelia Huck static const TypeInfo virtio_ccw_rng = {
17482db26d4cSKONRAD Frederic     .name          = TYPE_VIRTIO_RNG_CCW,
17492362ecc5SCornelia Huck     .parent        = TYPE_VIRTIO_CCW_DEVICE,
17502db26d4cSKONRAD Frederic     .instance_size = sizeof(VirtIORNGCcw),
17512db26d4cSKONRAD Frederic     .instance_init = virtio_ccw_rng_instance_init,
17522362ecc5SCornelia Huck     .class_init    = virtio_ccw_rng_class_init,
17532362ecc5SCornelia Huck };
17542362ecc5SCornelia Huck 
17555e5ced38SMarkus Armbruster static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp)
1756a5cf2bb4SCornelia Huck {
1757a5cf2bb4SCornelia Huck     VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
1758a5cf2bb4SCornelia Huck 
17591bf4d7aaSAndreas Färber     virtio_ccw_bus_new(&_dev->bus, sizeof(_dev->bus), _dev);
17601fa75523SCornelia Huck     virtio_ccw_device_realize(_dev, errp);
1761a5cf2bb4SCornelia Huck }
1762a5cf2bb4SCornelia Huck 
1763a5cf2bb4SCornelia Huck static int virtio_ccw_busdev_exit(DeviceState *dev)
1764a5cf2bb4SCornelia Huck {
1765a5cf2bb4SCornelia Huck     VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
1766a5cf2bb4SCornelia Huck     VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
1767a5cf2bb4SCornelia Huck 
1768a5cf2bb4SCornelia Huck     return _info->exit(_dev);
1769a5cf2bb4SCornelia Huck }
1770a5cf2bb4SCornelia Huck 
1771277bc95eSIgor Mammedov static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev,
1772277bc95eSIgor Mammedov                                      DeviceState *dev, Error **errp)
1773a5cf2bb4SCornelia Huck {
1774a5cf2bb4SCornelia Huck     VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
1775a5cf2bb4SCornelia Huck     SubchDev *sch = _dev->sch;
1776a5cf2bb4SCornelia Huck 
17770b81c1efSPaolo Bonzini     virtio_ccw_stop_ioeventfd(_dev);
17780b81c1efSPaolo Bonzini 
1779a5cf2bb4SCornelia Huck     /*
1780a5cf2bb4SCornelia Huck      * We should arrive here only for device_del, since we don't support
1781a5cf2bb4SCornelia Huck      * direct hot(un)plug of channels, but only through virtio.
1782a5cf2bb4SCornelia Huck      */
1783a5cf2bb4SCornelia Huck     assert(sch != NULL);
1784a5cf2bb4SCornelia Huck     /* Subchannel is now disabled and no longer valid. */
1785a5cf2bb4SCornelia Huck     sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA |
1786a5cf2bb4SCornelia Huck                                      PMCW_FLAGS_MASK_DNV);
1787a5cf2bb4SCornelia Huck 
1788a5cf2bb4SCornelia Huck     css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0);
1789a5cf2bb4SCornelia Huck 
179002a5c4c9SStefan Hajnoczi     object_unparent(OBJECT(dev));
1791a5cf2bb4SCornelia Huck }
1792a5cf2bb4SCornelia Huck 
1793a5cf2bb4SCornelia Huck static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
1794a5cf2bb4SCornelia Huck {
1795a5cf2bb4SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(klass);
1796a5cf2bb4SCornelia Huck 
17975e5ced38SMarkus Armbruster     dc->realize = virtio_ccw_busdev_realize;
1798a5cf2bb4SCornelia Huck     dc->exit = virtio_ccw_busdev_exit;
1799a5cf2bb4SCornelia Huck     dc->bus_type = TYPE_VIRTUAL_CSS_BUS;
1800a5cf2bb4SCornelia Huck }
1801a5cf2bb4SCornelia Huck 
1802a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_device_info = {
1803a5cf2bb4SCornelia Huck     .name = TYPE_VIRTIO_CCW_DEVICE,
1804a5cf2bb4SCornelia Huck     .parent = TYPE_DEVICE,
1805a5cf2bb4SCornelia Huck     .instance_size = sizeof(VirtioCcwDevice),
1806a5cf2bb4SCornelia Huck     .class_init = virtio_ccw_device_class_init,
1807a5cf2bb4SCornelia Huck     .class_size = sizeof(VirtIOCCWDeviceClass),
1808a5cf2bb4SCornelia Huck     .abstract = true,
1809a5cf2bb4SCornelia Huck };
1810a5cf2bb4SCornelia Huck 
1811a5cf2bb4SCornelia Huck /***************** Virtual-css Bus Bridge Device ********************/
1812a5cf2bb4SCornelia Huck /* Only required to have the virtio bus as child in the system bus */
1813a5cf2bb4SCornelia Huck 
1814a5cf2bb4SCornelia Huck static int virtual_css_bridge_init(SysBusDevice *dev)
1815a5cf2bb4SCornelia Huck {
1816a5cf2bb4SCornelia Huck     /* nothing */
1817a5cf2bb4SCornelia Huck     return 0;
1818a5cf2bb4SCornelia Huck }
1819a5cf2bb4SCornelia Huck 
1820a5cf2bb4SCornelia Huck static void virtual_css_bridge_class_init(ObjectClass *klass, void *data)
1821a5cf2bb4SCornelia Huck {
1822a5cf2bb4SCornelia Huck     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
1823277bc95eSIgor Mammedov     HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
1824cd20d616SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(klass);
1825a5cf2bb4SCornelia Huck 
1826a5cf2bb4SCornelia Huck     k->init = virtual_css_bridge_init;
1827277bc95eSIgor Mammedov     hc->unplug = virtio_ccw_busdev_unplug;
1828cd20d616SCornelia Huck     set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
1829a5cf2bb4SCornelia Huck }
1830a5cf2bb4SCornelia Huck 
1831a5cf2bb4SCornelia Huck static const TypeInfo virtual_css_bridge_info = {
1832a5cf2bb4SCornelia Huck     .name          = "virtual-css-bridge",
1833a5cf2bb4SCornelia Huck     .parent        = TYPE_SYS_BUS_DEVICE,
1834a5cf2bb4SCornelia Huck     .instance_size = sizeof(SysBusDevice),
1835a5cf2bb4SCornelia Huck     .class_init    = virtual_css_bridge_class_init,
1836277bc95eSIgor Mammedov     .interfaces = (InterfaceInfo[]) {
1837277bc95eSIgor Mammedov         { TYPE_HOTPLUG_HANDLER },
1838277bc95eSIgor Mammedov         { }
1839277bc95eSIgor Mammedov     }
1840a5cf2bb4SCornelia Huck };
1841a5cf2bb4SCornelia Huck 
1842a5cf2bb4SCornelia Huck /* virtio-ccw-bus */
1843a5cf2bb4SCornelia Huck 
18441bf4d7aaSAndreas Färber static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
18451bf4d7aaSAndreas Färber                                VirtioCcwDevice *dev)
1846a5cf2bb4SCornelia Huck {
1847a5cf2bb4SCornelia Huck     DeviceState *qdev = DEVICE(dev);
1848f4dd69aaSKONRAD Frederic     char virtio_bus_name[] = "virtio-bus";
1849a5cf2bb4SCornelia Huck 
1850fb17dfe0SAndreas Färber     qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_CCW_BUS,
1851fb17dfe0SAndreas Färber                         qdev, virtio_bus_name);
1852a5cf2bb4SCornelia Huck }
1853a5cf2bb4SCornelia Huck 
1854a5cf2bb4SCornelia Huck static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
1855a5cf2bb4SCornelia Huck {
1856a5cf2bb4SCornelia Huck     VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
1857a5cf2bb4SCornelia Huck     BusClass *bus_class = BUS_CLASS(klass);
1858a5cf2bb4SCornelia Huck 
1859a5cf2bb4SCornelia Huck     bus_class->max_dev = 1;
1860a5cf2bb4SCornelia Huck     k->notify = virtio_ccw_notify;
1861b4436a0bSCornelia Huck     k->vmstate_change = virtio_ccw_vmstate_change;
1862320ce850SCornelia Huck     k->query_guest_notifiers = virtio_ccw_query_guest_notifiers;
1863320ce850SCornelia Huck     k->set_host_notifier = virtio_ccw_set_host_notifier;
1864320ce850SCornelia Huck     k->set_guest_notifiers = virtio_ccw_set_guest_notifiers;
1865bcb2b582SJens Freimann     k->save_queue = virtio_ccw_save_queue;
1866bcb2b582SJens Freimann     k->load_queue = virtio_ccw_load_queue;
1867bcb2b582SJens Freimann     k->save_config = virtio_ccw_save_config;
1868bcb2b582SJens Freimann     k->load_config = virtio_ccw_load_config;
1869fb846a09SCornelia Huck     k->device_plugged = virtio_ccw_device_plugged;
1870fb846a09SCornelia Huck     k->device_unplugged = virtio_ccw_device_unplugged;
1871a5cf2bb4SCornelia Huck }
1872a5cf2bb4SCornelia Huck 
1873a5cf2bb4SCornelia Huck static const TypeInfo virtio_ccw_bus_info = {
1874a5cf2bb4SCornelia Huck     .name = TYPE_VIRTIO_CCW_BUS,
1875a5cf2bb4SCornelia Huck     .parent = TYPE_VIRTIO_BUS,
1876a5cf2bb4SCornelia Huck     .instance_size = sizeof(VirtioCcwBusState),
1877a5cf2bb4SCornelia Huck     .class_init = virtio_ccw_bus_class_init,
1878a5cf2bb4SCornelia Huck };
1879a5cf2bb4SCornelia Huck 
1880de6a9218SPierre Morel #ifdef CONFIG_VIRTFS
1881de6a9218SPierre Morel static Property virtio_ccw_9p_properties[] = {
1882de6a9218SPierre Morel     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
1883de6a9218SPierre Morel     DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
1884de6a9218SPierre Morel             VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
1885de6a9218SPierre Morel     DEFINE_PROP_END_OF_LIST(),
1886de6a9218SPierre Morel };
1887de6a9218SPierre Morel 
1888de6a9218SPierre Morel static void virtio_ccw_9p_realize(VirtioCcwDevice *ccw_dev, Error **errp)
1889de6a9218SPierre Morel {
1890de6a9218SPierre Morel     V9fsCCWState *dev = VIRTIO_9P_CCW(ccw_dev);
1891de6a9218SPierre Morel     DeviceState *vdev = DEVICE(&dev->vdev);
1892de6a9218SPierre Morel     Error *err = NULL;
1893de6a9218SPierre Morel 
1894de6a9218SPierre Morel     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
1895de6a9218SPierre Morel     object_property_set_bool(OBJECT(vdev), true, "realized", &err);
1896de6a9218SPierre Morel     if (err) {
1897de6a9218SPierre Morel         error_propagate(errp, err);
1898de6a9218SPierre Morel     }
1899de6a9218SPierre Morel }
1900de6a9218SPierre Morel 
1901de6a9218SPierre Morel static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data)
1902de6a9218SPierre Morel {
1903de6a9218SPierre Morel     DeviceClass *dc = DEVICE_CLASS(klass);
1904de6a9218SPierre Morel     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
1905de6a9218SPierre Morel 
1906de6a9218SPierre Morel     k->exit = virtio_ccw_exit;
1907de6a9218SPierre Morel     k->realize = virtio_ccw_9p_realize;
1908de6a9218SPierre Morel     dc->reset = virtio_ccw_reset;
1909de6a9218SPierre Morel     dc->props = virtio_ccw_9p_properties;
1910de6a9218SPierre Morel     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
1911de6a9218SPierre Morel }
1912de6a9218SPierre Morel 
1913de6a9218SPierre Morel static void virtio_ccw_9p_instance_init(Object *obj)
1914de6a9218SPierre Morel {
1915de6a9218SPierre Morel     V9fsCCWState *dev = VIRTIO_9P_CCW(obj);
1916de6a9218SPierre Morel 
1917de6a9218SPierre Morel     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
1918de6a9218SPierre Morel                                 TYPE_VIRTIO_9P);
1919de6a9218SPierre Morel }
1920de6a9218SPierre Morel 
1921de6a9218SPierre Morel static const TypeInfo virtio_ccw_9p_info = {
1922de6a9218SPierre Morel     .name          = TYPE_VIRTIO_9P_CCW,
1923de6a9218SPierre Morel     .parent        = TYPE_VIRTIO_CCW_DEVICE,
1924de6a9218SPierre Morel     .instance_size = sizeof(V9fsCCWState),
1925de6a9218SPierre Morel     .instance_init = virtio_ccw_9p_instance_init,
1926de6a9218SPierre Morel     .class_init    = virtio_ccw_9p_class_init,
1927de6a9218SPierre Morel };
1928de6a9218SPierre Morel #endif
1929de6a9218SPierre Morel 
1930a5cf2bb4SCornelia Huck static void virtio_ccw_register(void)
1931a5cf2bb4SCornelia Huck {
1932a5cf2bb4SCornelia Huck     type_register_static(&virtio_ccw_bus_info);
1933a5cf2bb4SCornelia Huck     type_register_static(&virtual_css_bus_info);
1934a5cf2bb4SCornelia Huck     type_register_static(&virtio_ccw_device_info);
1935a5cf2bb4SCornelia Huck     type_register_static(&virtio_ccw_serial);
1936a5cf2bb4SCornelia Huck     type_register_static(&virtio_ccw_blk);
1937a5cf2bb4SCornelia Huck     type_register_static(&virtio_ccw_net);
1938a5cf2bb4SCornelia Huck     type_register_static(&virtio_ccw_balloon);
1939a5cf2bb4SCornelia Huck     type_register_static(&virtio_ccw_scsi);
1940b702d2aeSEd Maste #ifdef CONFIG_VHOST_SCSI
1941ccf6916cSPaolo Bonzini     type_register_static(&vhost_ccw_scsi);
1942b702d2aeSEd Maste #endif
19432362ecc5SCornelia Huck     type_register_static(&virtio_ccw_rng);
1944a5cf2bb4SCornelia Huck     type_register_static(&virtual_css_bridge_info);
1945de6a9218SPierre Morel #ifdef CONFIG_VIRTFS
1946de6a9218SPierre Morel     type_register_static(&virtio_ccw_9p_info);
1947de6a9218SPierre Morel #endif
1948a5cf2bb4SCornelia Huck }
1949a5cf2bb4SCornelia Huck 
1950a5cf2bb4SCornelia Huck type_init(virtio_ccw_register)
1951