xref: /qemu/hw/intc/s390_flic_kvm.c (revision e7be8d499700f1ebdbfe8094aa19237998c0e1e3)
17b35d0c4SCornelia Huck /*
27b35d0c4SCornelia Huck  * QEMU S390x KVM floating interrupt controller (flic)
37b35d0c4SCornelia Huck  *
47b35d0c4SCornelia Huck  * Copyright 2014 IBM Corp.
57b35d0c4SCornelia Huck  * Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
67b35d0c4SCornelia Huck  *            Cornelia Huck <cornelia.huck@de.ibm.com>
77b35d0c4SCornelia Huck  *
87b35d0c4SCornelia Huck  * This work is licensed under the terms of the GNU GPL, version 2 or (at
97b35d0c4SCornelia Huck  * your option) any later version. See the COPYING file in the top-level
107b35d0c4SCornelia Huck  * directory.
117b35d0c4SCornelia Huck  */
127b35d0c4SCornelia Huck 
1390191d07SPeter Maydell #include "qemu/osdep.h"
1433c11879SPaolo Bonzini #include "qemu-common.h"
1533c11879SPaolo Bonzini #include "cpu.h"
167b35d0c4SCornelia Huck #include <sys/ioctl.h>
177b35d0c4SCornelia Huck #include "qemu/error-report.h"
18f62f2109SHalil Pasic #include "qapi/error.h"
197b35d0c4SCornelia Huck #include "hw/sysbus.h"
207b35d0c4SCornelia Huck #include "sysemu/kvm.h"
217b35d0c4SCornelia Huck #include "hw/s390x/s390_flic.h"
22d426d9fbSCornelia Huck #include "hw/s390x/adapter.h"
236c1dd652SFei Li #include "hw/s390x/css.h"
247b35d0c4SCornelia Huck #include "trace.h"
257b35d0c4SCornelia Huck 
267b35d0c4SCornelia Huck #define FLIC_SAVE_INITIAL_SIZE getpagesize()
277b35d0c4SCornelia Huck #define FLIC_FAILED (-1UL)
287b35d0c4SCornelia Huck #define FLIC_SAVEVM_VERSION 1
297b35d0c4SCornelia Huck 
307b35d0c4SCornelia Huck typedef struct KVMS390FLICState {
317b35d0c4SCornelia Huck     S390FLICState parent_obj;
327b35d0c4SCornelia Huck 
337b35d0c4SCornelia Huck     uint32_t fd;
349eccb862SHalil Pasic     bool clear_io_supported;
357b35d0c4SCornelia Huck } KVMS390FLICState;
367b35d0c4SCornelia Huck 
377b35d0c4SCornelia Huck DeviceState *s390_flic_kvm_create(void)
387b35d0c4SCornelia Huck {
397b35d0c4SCornelia Huck     DeviceState *dev = NULL;
407b35d0c4SCornelia Huck 
417b35d0c4SCornelia Huck     if (kvm_enabled()) {
427b35d0c4SCornelia Huck         dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
437b35d0c4SCornelia Huck         object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
447b35d0c4SCornelia Huck                                   OBJECT(dev), NULL);
457b35d0c4SCornelia Huck     }
467b35d0c4SCornelia Huck     return dev;
477b35d0c4SCornelia Huck }
487b35d0c4SCornelia Huck 
497b35d0c4SCornelia Huck /**
507b35d0c4SCornelia Huck  * flic_get_all_irqs - store all pending irqs in buffer
517b35d0c4SCornelia Huck  * @buf: pointer to buffer which is passed to kernel
527b35d0c4SCornelia Huck  * @len: length of buffer
537b35d0c4SCornelia Huck  * @flic: pointer to flic device state
547b35d0c4SCornelia Huck  *
557b35d0c4SCornelia Huck  * Returns: -ENOMEM if buffer is too small,
567b35d0c4SCornelia Huck  * -EINVAL if attr.group is invalid,
577b35d0c4SCornelia Huck  * -EFAULT if copying to userspace failed,
587b35d0c4SCornelia Huck  * on success return number of stored interrupts
597b35d0c4SCornelia Huck  */
607b35d0c4SCornelia Huck static int flic_get_all_irqs(KVMS390FLICState *flic,
617b35d0c4SCornelia Huck                              void *buf, int len)
627b35d0c4SCornelia Huck {
637b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
647b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_GET_ALL_IRQS,
657b35d0c4SCornelia Huck         .addr = (uint64_t) buf,
667b35d0c4SCornelia Huck         .attr = len,
677b35d0c4SCornelia Huck     };
687b35d0c4SCornelia Huck     int rc;
697b35d0c4SCornelia Huck 
707b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
717b35d0c4SCornelia Huck 
727b35d0c4SCornelia Huck     return rc == -1 ? -errno : rc;
737b35d0c4SCornelia Huck }
747b35d0c4SCornelia Huck 
757b35d0c4SCornelia Huck static void flic_enable_pfault(KVMS390FLICState *flic)
767b35d0c4SCornelia Huck {
777b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
787b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_APF_ENABLE,
797b35d0c4SCornelia Huck     };
807b35d0c4SCornelia Huck     int rc;
817b35d0c4SCornelia Huck 
827b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
837b35d0c4SCornelia Huck 
847b35d0c4SCornelia Huck     if (rc) {
857b35d0c4SCornelia Huck         fprintf(stderr, "flic: couldn't enable pfault\n");
867b35d0c4SCornelia Huck     }
877b35d0c4SCornelia Huck }
887b35d0c4SCornelia Huck 
897b35d0c4SCornelia Huck static void flic_disable_wait_pfault(KVMS390FLICState *flic)
907b35d0c4SCornelia Huck {
917b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
927b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
937b35d0c4SCornelia Huck     };
947b35d0c4SCornelia Huck     int rc;
957b35d0c4SCornelia Huck 
967b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
977b35d0c4SCornelia Huck 
987b35d0c4SCornelia Huck     if (rc) {
997b35d0c4SCornelia Huck         fprintf(stderr, "flic: couldn't disable pfault\n");
1007b35d0c4SCornelia Huck     }
1017b35d0c4SCornelia Huck }
1027b35d0c4SCornelia Huck 
1037b35d0c4SCornelia Huck /** flic_enqueue_irqs - returns 0 on success
1047b35d0c4SCornelia Huck  * @buf: pointer to buffer which is passed to kernel
1057b35d0c4SCornelia Huck  * @len: length of buffer
1067b35d0c4SCornelia Huck  * @flic: pointer to flic device state
1077b35d0c4SCornelia Huck  *
1087b35d0c4SCornelia Huck  * Returns: -EINVAL if attr.group is unknown
1097b35d0c4SCornelia Huck  */
1107b35d0c4SCornelia Huck static int flic_enqueue_irqs(void *buf, uint64_t len,
1117b35d0c4SCornelia Huck                             KVMS390FLICState *flic)
1127b35d0c4SCornelia Huck {
1137b35d0c4SCornelia Huck     int rc;
1147b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
1157b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_ENQUEUE,
1167b35d0c4SCornelia Huck         .addr = (uint64_t) buf,
1177b35d0c4SCornelia Huck         .attr = len,
1187b35d0c4SCornelia Huck     };
1197b35d0c4SCornelia Huck 
1207b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
1217b35d0c4SCornelia Huck 
1227b35d0c4SCornelia Huck     return rc ? -errno : 0;
1237b35d0c4SCornelia Huck }
1247b35d0c4SCornelia Huck 
125bbd8bb8eSCornelia Huck int kvm_s390_inject_flic(struct kvm_s390_irq *irq)
126bbd8bb8eSCornelia Huck {
127bbd8bb8eSCornelia Huck     static KVMS390FLICState *flic;
128bbd8bb8eSCornelia Huck 
129bbd8bb8eSCornelia Huck     if (unlikely(!flic)) {
130bbd8bb8eSCornelia Huck         flic = KVM_S390_FLIC(s390_get_flic());
131bbd8bb8eSCornelia Huck     }
132bbd8bb8eSCornelia Huck     return flic_enqueue_irqs(irq, sizeof(*irq), flic);
133bbd8bb8eSCornelia Huck }
134bbd8bb8eSCornelia Huck 
1359eccb862SHalil Pasic static int kvm_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id,
1369eccb862SHalil Pasic                            uint16_t subchannel_nr)
1379eccb862SHalil Pasic {
1389eccb862SHalil Pasic     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
1399eccb862SHalil Pasic     int rc;
1409eccb862SHalil Pasic     uint32_t sid = subchannel_id << 16 | subchannel_nr;
1419eccb862SHalil Pasic     struct kvm_device_attr attr = {
1429eccb862SHalil Pasic         .group = KVM_DEV_FLIC_CLEAR_IO_IRQ,
1439eccb862SHalil Pasic         .addr = (uint64_t) &sid,
1449eccb862SHalil Pasic         .attr = sizeof(sid),
1459eccb862SHalil Pasic     };
1469eccb862SHalil Pasic     if (unlikely(!flic->clear_io_supported)) {
1479eccb862SHalil Pasic         return -ENOSYS;
1489eccb862SHalil Pasic     }
1499eccb862SHalil Pasic     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
1509eccb862SHalil Pasic     return rc ? -errno : 0;
1519eccb862SHalil Pasic }
1529eccb862SHalil Pasic 
1536c1dd652SFei Li static int kvm_s390_modify_ais_mode(S390FLICState *fs, uint8_t isc,
1546c1dd652SFei Li                                     uint16_t mode)
1556c1dd652SFei Li {
1566c1dd652SFei Li     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
1576c1dd652SFei Li     struct kvm_s390_ais_req req = {
1586c1dd652SFei Li         .isc = isc,
1596c1dd652SFei Li         .mode = mode,
1606c1dd652SFei Li     };
1616c1dd652SFei Li     struct kvm_device_attr attr = {
1626c1dd652SFei Li         .group = KVM_DEV_FLIC_AISM,
1636c1dd652SFei Li         .addr = (uint64_t)&req,
1646c1dd652SFei Li     };
1656c1dd652SFei Li 
1666c1dd652SFei Li     if (!fs->ais_supported) {
1676c1dd652SFei Li         return -ENOSYS;
1686c1dd652SFei Li     }
1696c1dd652SFei Li 
1706c1dd652SFei Li     return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0;
1716c1dd652SFei Li }
1726c1dd652SFei Li 
1731622ffd5SYi Min Zhao static int kvm_s390_inject_airq(S390FLICState *fs, uint8_t type,
1741622ffd5SYi Min Zhao                                 uint8_t isc, uint8_t flags)
1751622ffd5SYi Min Zhao {
1761622ffd5SYi Min Zhao     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
1771622ffd5SYi Min Zhao     uint32_t id = css_get_adapter_id(type, isc);
1781622ffd5SYi Min Zhao     struct kvm_device_attr attr = {
1791622ffd5SYi Min Zhao         .group = KVM_DEV_FLIC_AIRQ_INJECT,
1801622ffd5SYi Min Zhao         .attr = id,
1811622ffd5SYi Min Zhao     };
1821622ffd5SYi Min Zhao 
1831622ffd5SYi Min Zhao     if (!fs->ais_supported) {
1841622ffd5SYi Min Zhao         return -ENOSYS;
1851622ffd5SYi Min Zhao     }
1861622ffd5SYi Min Zhao 
1871622ffd5SYi Min Zhao     return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0;
1881622ffd5SYi Min Zhao }
1891622ffd5SYi Min Zhao 
1907b35d0c4SCornelia Huck /**
1917b35d0c4SCornelia Huck  * __get_all_irqs - store all pending irqs in buffer
1927b35d0c4SCornelia Huck  * @flic: pointer to flic device state
1937b35d0c4SCornelia Huck  * @buf: pointer to pointer to a buffer
1947b35d0c4SCornelia Huck  * @len: length of buffer
1957b35d0c4SCornelia Huck  *
1967b35d0c4SCornelia Huck  * Returns: return value of flic_get_all_irqs
1977b35d0c4SCornelia Huck  * Note: Retry and increase buffer size until flic_get_all_irqs
1987b35d0c4SCornelia Huck  * either returns a value >= 0 or a negative error code.
1997b35d0c4SCornelia Huck  * -ENOMEM is an exception, which means the buffer is too small
2007b35d0c4SCornelia Huck  * and we should try again. Other negative error codes can be
2017b35d0c4SCornelia Huck  * -EFAULT and -EINVAL which we ignore at this point
2027b35d0c4SCornelia Huck  */
2037b35d0c4SCornelia Huck static int __get_all_irqs(KVMS390FLICState *flic,
2047b35d0c4SCornelia Huck                           void **buf, int len)
2057b35d0c4SCornelia Huck {
2067b35d0c4SCornelia Huck     int r;
2077b35d0c4SCornelia Huck 
2087b35d0c4SCornelia Huck     do {
2097b35d0c4SCornelia Huck         /* returns -ENOMEM if buffer is too small and number
2107b35d0c4SCornelia Huck          * of queued interrupts on success */
2117b35d0c4SCornelia Huck         r = flic_get_all_irqs(flic, *buf, len);
2127b35d0c4SCornelia Huck         if (r >= 0) {
2137b35d0c4SCornelia Huck             break;
2147b35d0c4SCornelia Huck         }
2157b35d0c4SCornelia Huck         len *= 2;
2167b35d0c4SCornelia Huck         *buf = g_try_realloc(*buf, len);
2177b35d0c4SCornelia Huck         if (!buf) {
2187b35d0c4SCornelia Huck             return -ENOMEM;
2197b35d0c4SCornelia Huck         }
2207b35d0c4SCornelia Huck     } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
2217b35d0c4SCornelia Huck 
2227b35d0c4SCornelia Huck     return r;
2237b35d0c4SCornelia Huck }
2247b35d0c4SCornelia Huck 
22503cf077aSCornelia Huck static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
22603cf077aSCornelia Huck                                         uint8_t isc, bool swap,
2271497c160SFei Li                                         bool is_maskable, uint8_t flags)
22803cf077aSCornelia Huck {
22903cf077aSCornelia Huck     struct kvm_s390_io_adapter adapter = {
23003cf077aSCornelia Huck         .id = id,
23103cf077aSCornelia Huck         .isc = isc,
23203cf077aSCornelia Huck         .maskable = is_maskable,
23303cf077aSCornelia Huck         .swap = swap,
2341497c160SFei Li         .flags = flags,
23503cf077aSCornelia Huck     };
23603cf077aSCornelia Huck     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
2379be38598SEduardo Habkost     int r;
23803cf077aSCornelia Huck     struct kvm_device_attr attr = {
23903cf077aSCornelia Huck         .group = KVM_DEV_FLIC_ADAPTER_REGISTER,
24003cf077aSCornelia Huck         .addr = (uint64_t)&adapter,
24103cf077aSCornelia Huck     };
24203cf077aSCornelia Huck 
2434cbd6c41SFei Li     if (!kvm_gsi_routing_enabled()) {
24408da527fSCornelia Huck         /* nothing to do */
24508da527fSCornelia Huck         return 0;
24603cf077aSCornelia Huck     }
24703cf077aSCornelia Huck 
24803cf077aSCornelia Huck     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
24903cf077aSCornelia Huck 
2509be38598SEduardo Habkost     return r ? -errno : 0;
25103cf077aSCornelia Huck }
25203cf077aSCornelia Huck 
253d426d9fbSCornelia Huck static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
254d426d9fbSCornelia Huck                                    uint64_t map_addr, bool do_map)
255d426d9fbSCornelia Huck {
256d426d9fbSCornelia Huck     struct kvm_s390_io_adapter_req req = {
257d426d9fbSCornelia Huck         .id = id,
258d426d9fbSCornelia Huck         .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP,
259d426d9fbSCornelia Huck         .addr = map_addr,
260d426d9fbSCornelia Huck     };
261d426d9fbSCornelia Huck     struct kvm_device_attr attr = {
262d426d9fbSCornelia Huck         .group = KVM_DEV_FLIC_ADAPTER_MODIFY,
263d426d9fbSCornelia Huck         .addr = (uint64_t)&req,
264d426d9fbSCornelia Huck     };
265d426d9fbSCornelia Huck     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
266d426d9fbSCornelia Huck     int r;
267d426d9fbSCornelia Huck 
2684cbd6c41SFei Li     if (!kvm_gsi_routing_enabled()) {
26908da527fSCornelia Huck         /* nothing to do */
27008da527fSCornelia Huck         return 0;
271d426d9fbSCornelia Huck     }
272d426d9fbSCornelia Huck 
273d426d9fbSCornelia Huck     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
274d426d9fbSCornelia Huck     return r ? -errno : 0;
275d426d9fbSCornelia Huck }
276d426d9fbSCornelia Huck 
277d426d9fbSCornelia Huck static int kvm_s390_add_adapter_routes(S390FLICState *fs,
278d426d9fbSCornelia Huck                                        AdapterRoutes *routes)
279d426d9fbSCornelia Huck {
280d426d9fbSCornelia Huck     int ret, i;
281d426d9fbSCornelia Huck     uint64_t ind_offset = routes->adapter.ind_offset;
282d426d9fbSCornelia Huck 
283d426d9fbSCornelia Huck     for (i = 0; i < routes->num_routes; i++) {
284d426d9fbSCornelia Huck         ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter);
285d426d9fbSCornelia Huck         if (ret < 0) {
286d426d9fbSCornelia Huck             goto out_undo;
287d426d9fbSCornelia Huck         }
288d426d9fbSCornelia Huck         routes->gsi[i] = ret;
289d426d9fbSCornelia Huck         routes->adapter.ind_offset++;
290d426d9fbSCornelia Huck     }
291c0194a00SJens Freimann     kvm_irqchip_commit_routes(kvm_state);
292c0194a00SJens Freimann 
293d426d9fbSCornelia Huck     /* Restore passed-in structure to original state. */
294d426d9fbSCornelia Huck     routes->adapter.ind_offset = ind_offset;
295d426d9fbSCornelia Huck     return 0;
296d426d9fbSCornelia Huck out_undo:
297d426d9fbSCornelia Huck     while (--i >= 0) {
298d426d9fbSCornelia Huck         kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
299d426d9fbSCornelia Huck         routes->gsi[i] = -1;
300d426d9fbSCornelia Huck     }
301d426d9fbSCornelia Huck     routes->adapter.ind_offset = ind_offset;
302d426d9fbSCornelia Huck     return ret;
303d426d9fbSCornelia Huck }
304d426d9fbSCornelia Huck 
305d426d9fbSCornelia Huck static void kvm_s390_release_adapter_routes(S390FLICState *fs,
306d426d9fbSCornelia Huck                                             AdapterRoutes *routes)
307d426d9fbSCornelia Huck {
308d426d9fbSCornelia Huck     int i;
309d426d9fbSCornelia Huck 
310d426d9fbSCornelia Huck     for (i = 0; i < routes->num_routes; i++) {
311d426d9fbSCornelia Huck         if (routes->gsi[i] >= 0) {
312d426d9fbSCornelia Huck             kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
313d426d9fbSCornelia Huck             routes->gsi[i] = -1;
314d426d9fbSCornelia Huck         }
315d426d9fbSCornelia Huck     }
316d426d9fbSCornelia Huck }
317d426d9fbSCornelia Huck 
3187b35d0c4SCornelia Huck /**
3197b35d0c4SCornelia Huck  * kvm_flic_save - Save pending floating interrupts
3207b35d0c4SCornelia Huck  * @f: QEMUFile containing migration state
3217b35d0c4SCornelia Huck  * @opaque: pointer to flic device state
322f2cab7f1SCornelia Huck  * @size: ignored
3237b35d0c4SCornelia Huck  *
3247b35d0c4SCornelia Huck  * Note: Pass buf and len to kernel. Start with one page and
3257b35d0c4SCornelia Huck  * increase until buffer is sufficient or maxium size is
3267b35d0c4SCornelia Huck  * reached
3277b35d0c4SCornelia Huck  */
3282c21ee76SJianjun Duan static int kvm_flic_save(QEMUFile *f, void *opaque, size_t size,
3292c21ee76SJianjun Duan                          VMStateField *field, QJSON *vmdesc)
3307b35d0c4SCornelia Huck {
3317b35d0c4SCornelia Huck     KVMS390FLICState *flic = opaque;
3327b35d0c4SCornelia Huck     int len = FLIC_SAVE_INITIAL_SIZE;
3337b35d0c4SCornelia Huck     void *buf;
3347b35d0c4SCornelia Huck     int count;
335ba690c71SCornelia Huck     int r = 0;
3367b35d0c4SCornelia Huck 
3377b35d0c4SCornelia Huck     flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
3387b35d0c4SCornelia Huck 
3397b35d0c4SCornelia Huck     buf = g_try_malloc0(len);
3407b35d0c4SCornelia Huck     if (!buf) {
3417b35d0c4SCornelia Huck         /* Storing FLIC_FAILED into the count field here will cause the
3427b35d0c4SCornelia Huck          * target system to fail when attempting to load irqs from the
3437b35d0c4SCornelia Huck          * migration state */
3447b35d0c4SCornelia Huck         error_report("flic: couldn't allocate memory");
3457b35d0c4SCornelia Huck         qemu_put_be64(f, FLIC_FAILED);
346ba690c71SCornelia Huck         return -ENOMEM;
3477b35d0c4SCornelia Huck     }
3487b35d0c4SCornelia Huck 
3497b35d0c4SCornelia Huck     count = __get_all_irqs(flic, &buf, len);
3507b35d0c4SCornelia Huck     if (count < 0) {
3517b35d0c4SCornelia Huck         error_report("flic: couldn't retrieve irqs from kernel, rc %d",
3527b35d0c4SCornelia Huck                      count);
3537b35d0c4SCornelia Huck         /* Storing FLIC_FAILED into the count field here will cause the
3547b35d0c4SCornelia Huck          * target system to fail when attempting to load irqs from the
3557b35d0c4SCornelia Huck          * migration state */
3567b35d0c4SCornelia Huck         qemu_put_be64(f, FLIC_FAILED);
357ba690c71SCornelia Huck         r = count;
3587b35d0c4SCornelia Huck     } else {
3597b35d0c4SCornelia Huck         qemu_put_be64(f, count);
3607b35d0c4SCornelia Huck         qemu_put_buffer(f, (uint8_t *) buf,
3617b35d0c4SCornelia Huck                         count * sizeof(struct kvm_s390_irq));
3627b35d0c4SCornelia Huck     }
3637b35d0c4SCornelia Huck     g_free(buf);
3642c21ee76SJianjun Duan 
365ba690c71SCornelia Huck     return r;
3667b35d0c4SCornelia Huck }
3677b35d0c4SCornelia Huck 
3687b35d0c4SCornelia Huck /**
3697b35d0c4SCornelia Huck  * kvm_flic_load - Load pending floating interrupts
3707b35d0c4SCornelia Huck  * @f: QEMUFile containing migration state
3717b35d0c4SCornelia Huck  * @opaque: pointer to flic device state
372f2cab7f1SCornelia Huck  * @size: ignored
3737b35d0c4SCornelia Huck  *
3747b35d0c4SCornelia Huck  * Returns: value of flic_enqueue_irqs, -EINVAL on error
3757b35d0c4SCornelia Huck  * Note: Do nothing when no interrupts where stored
3767b35d0c4SCornelia Huck  * in QEMUFile
3777b35d0c4SCornelia Huck  */
3782c21ee76SJianjun Duan static int kvm_flic_load(QEMUFile *f, void *opaque, size_t size,
3792c21ee76SJianjun Duan                          VMStateField *field)
3807b35d0c4SCornelia Huck {
3817b35d0c4SCornelia Huck     uint64_t len = 0;
3827b35d0c4SCornelia Huck     uint64_t count = 0;
3837b35d0c4SCornelia Huck     void *buf = NULL;
3847b35d0c4SCornelia Huck     int r = 0;
3857b35d0c4SCornelia Huck 
3867b35d0c4SCornelia Huck     flic_enable_pfault((struct KVMS390FLICState *) opaque);
3877b35d0c4SCornelia Huck 
3887b35d0c4SCornelia Huck     count = qemu_get_be64(f);
3897b35d0c4SCornelia Huck     len = count * sizeof(struct kvm_s390_irq);
3907b35d0c4SCornelia Huck     if (count == FLIC_FAILED) {
3917b35d0c4SCornelia Huck         r = -EINVAL;
3927b35d0c4SCornelia Huck         goto out;
3937b35d0c4SCornelia Huck     }
3947b35d0c4SCornelia Huck     if (count == 0) {
3957b35d0c4SCornelia Huck         r = 0;
3967b35d0c4SCornelia Huck         goto out;
3977b35d0c4SCornelia Huck     }
3987b35d0c4SCornelia Huck     buf = g_try_malloc0(len);
3997b35d0c4SCornelia Huck     if (!buf) {
4007b35d0c4SCornelia Huck         r = -ENOMEM;
4017b35d0c4SCornelia Huck         goto out;
4027b35d0c4SCornelia Huck     }
4037b35d0c4SCornelia Huck 
4047b35d0c4SCornelia Huck     if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
4057b35d0c4SCornelia Huck         r = -EINVAL;
4067b35d0c4SCornelia Huck         goto out_free;
4077b35d0c4SCornelia Huck     }
4087b35d0c4SCornelia Huck     r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
4097b35d0c4SCornelia Huck 
4107b35d0c4SCornelia Huck out_free:
4117b35d0c4SCornelia Huck     g_free(buf);
4127b35d0c4SCornelia Huck out:
4137b35d0c4SCornelia Huck     return r;
4147b35d0c4SCornelia Huck }
4157b35d0c4SCornelia Huck 
416*e7be8d49SYi Min Zhao typedef struct KVMS390FLICStateMigTmp {
417*e7be8d49SYi Min Zhao     KVMS390FLICState *parent;
418*e7be8d49SYi Min Zhao     uint8_t simm;
419*e7be8d49SYi Min Zhao     uint8_t nimm;
420*e7be8d49SYi Min Zhao } KVMS390FLICStateMigTmp;
421*e7be8d49SYi Min Zhao 
422*e7be8d49SYi Min Zhao static void kvm_flic_ais_pre_save(void *opaque)
423*e7be8d49SYi Min Zhao {
424*e7be8d49SYi Min Zhao     KVMS390FLICStateMigTmp *tmp = opaque;
425*e7be8d49SYi Min Zhao     KVMS390FLICState *flic = tmp->parent;
426*e7be8d49SYi Min Zhao     struct kvm_s390_ais_all ais;
427*e7be8d49SYi Min Zhao     struct kvm_device_attr attr = {
428*e7be8d49SYi Min Zhao         .group = KVM_DEV_FLIC_AISM_ALL,
429*e7be8d49SYi Min Zhao         .addr = (uint64_t)&ais,
430*e7be8d49SYi Min Zhao         .attr = sizeof(ais),
431*e7be8d49SYi Min Zhao     };
432*e7be8d49SYi Min Zhao 
433*e7be8d49SYi Min Zhao     if (ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr)) {
434*e7be8d49SYi Min Zhao         error_report("Failed to retrieve kvm flic ais states");
435*e7be8d49SYi Min Zhao         return;
436*e7be8d49SYi Min Zhao     }
437*e7be8d49SYi Min Zhao 
438*e7be8d49SYi Min Zhao     tmp->simm = ais.simm;
439*e7be8d49SYi Min Zhao     tmp->nimm = ais.nimm;
440*e7be8d49SYi Min Zhao }
441*e7be8d49SYi Min Zhao 
442*e7be8d49SYi Min Zhao static int kvm_flic_ais_post_load(void *opaque, int version_id)
443*e7be8d49SYi Min Zhao {
444*e7be8d49SYi Min Zhao     KVMS390FLICStateMigTmp *tmp = opaque;
445*e7be8d49SYi Min Zhao     KVMS390FLICState *flic = tmp->parent;
446*e7be8d49SYi Min Zhao     struct kvm_s390_ais_all ais = {
447*e7be8d49SYi Min Zhao         .simm = tmp->simm,
448*e7be8d49SYi Min Zhao         .nimm = tmp->nimm,
449*e7be8d49SYi Min Zhao     };
450*e7be8d49SYi Min Zhao     struct kvm_device_attr attr = {
451*e7be8d49SYi Min Zhao         .group = KVM_DEV_FLIC_AISM_ALL,
452*e7be8d49SYi Min Zhao         .addr = (uint64_t)&ais,
453*e7be8d49SYi Min Zhao     };
454*e7be8d49SYi Min Zhao 
455*e7be8d49SYi Min Zhao     /* This can happen when the user mis-configures its guests in an
456*e7be8d49SYi Min Zhao      * incompatible fashion or without a CPU model. For example using
457*e7be8d49SYi Min Zhao      * qemu with -cpu host (which is not migration safe) and do a
458*e7be8d49SYi Min Zhao      * migration from a host that has AIS to a host that has no AIS.
459*e7be8d49SYi Min Zhao      * In that case the target system will reject the migration here.
460*e7be8d49SYi Min Zhao      */
461*e7be8d49SYi Min Zhao     if (!ais_needed(flic)) {
462*e7be8d49SYi Min Zhao         return -ENOSYS;
463*e7be8d49SYi Min Zhao     }
464*e7be8d49SYi Min Zhao 
465*e7be8d49SYi Min Zhao     return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0;
466*e7be8d49SYi Min Zhao }
467*e7be8d49SYi Min Zhao 
468*e7be8d49SYi Min Zhao static const VMStateDescription kvm_s390_flic_ais_tmp = {
469*e7be8d49SYi Min Zhao     .name = "s390-flic-ais-tmp",
470*e7be8d49SYi Min Zhao     .pre_save = kvm_flic_ais_pre_save,
471*e7be8d49SYi Min Zhao     .post_load = kvm_flic_ais_post_load,
472*e7be8d49SYi Min Zhao     .fields = (VMStateField[]) {
473*e7be8d49SYi Min Zhao         VMSTATE_UINT8(simm, KVMS390FLICStateMigTmp),
474*e7be8d49SYi Min Zhao         VMSTATE_UINT8(nimm, KVMS390FLICStateMigTmp),
475*e7be8d49SYi Min Zhao         VMSTATE_END_OF_LIST()
476*e7be8d49SYi Min Zhao     }
477*e7be8d49SYi Min Zhao };
478*e7be8d49SYi Min Zhao 
479*e7be8d49SYi Min Zhao static const VMStateDescription kvm_s390_flic_vmstate_ais = {
480*e7be8d49SYi Min Zhao     .name = "s390-flic/ais",
481*e7be8d49SYi Min Zhao     .version_id = 1,
482*e7be8d49SYi Min Zhao     .minimum_version_id = 1,
483*e7be8d49SYi Min Zhao     .needed = ais_needed,
484*e7be8d49SYi Min Zhao     .fields = (VMStateField[]) {
485*e7be8d49SYi Min Zhao         VMSTATE_WITH_TMP(KVMS390FLICState, KVMS390FLICStateMigTmp,
486*e7be8d49SYi Min Zhao                          kvm_s390_flic_ais_tmp),
487*e7be8d49SYi Min Zhao         VMSTATE_END_OF_LIST()
488*e7be8d49SYi Min Zhao     }
489*e7be8d49SYi Min Zhao };
490*e7be8d49SYi Min Zhao 
491f2cab7f1SCornelia Huck static const VMStateDescription kvm_s390_flic_vmstate = {
492*e7be8d49SYi Min Zhao     /* should have been like kvm-s390-flic,
493*e7be8d49SYi Min Zhao      * can't change without breaking compat */
494f2cab7f1SCornelia Huck     .name = "s390-flic",
495f2cab7f1SCornelia Huck     .version_id = FLIC_SAVEVM_VERSION,
496f2cab7f1SCornelia Huck     .minimum_version_id = FLIC_SAVEVM_VERSION,
497f2cab7f1SCornelia Huck     .fields = (VMStateField[]) {
498f2cab7f1SCornelia Huck         {
499f2cab7f1SCornelia Huck             .name = "irqs",
500f2cab7f1SCornelia Huck             .info = &(const VMStateInfo) {
501f2cab7f1SCornelia Huck                 .name = "irqs",
502f2cab7f1SCornelia Huck                 .get = kvm_flic_load,
503f2cab7f1SCornelia Huck                 .put = kvm_flic_save,
504f2cab7f1SCornelia Huck             },
505f2cab7f1SCornelia Huck             .flags = VMS_SINGLE,
506f2cab7f1SCornelia Huck         },
507f2cab7f1SCornelia Huck         VMSTATE_END_OF_LIST()
508*e7be8d49SYi Min Zhao     },
509*e7be8d49SYi Min Zhao     .subsections = (const VMStateDescription * []) {
510*e7be8d49SYi Min Zhao         &kvm_s390_flic_vmstate_ais,
511*e7be8d49SYi Min Zhao         NULL
512f2cab7f1SCornelia Huck     }
513f2cab7f1SCornelia Huck };
514f2cab7f1SCornelia Huck 
5155cbab1bfSHalil Pasic typedef struct KVMS390FLICStateClass {
5165cbab1bfSHalil Pasic     S390FLICStateClass parent_class;
5175cbab1bfSHalil Pasic     DeviceRealize parent_realize;
5185cbab1bfSHalil Pasic } KVMS390FLICStateClass;
5195cbab1bfSHalil Pasic 
5205cbab1bfSHalil Pasic #define KVM_S390_FLIC_GET_CLASS(obj) \
5215cbab1bfSHalil Pasic     OBJECT_GET_CLASS(KVMS390FLICStateClass, (obj), TYPE_KVM_S390_FLIC)
5225cbab1bfSHalil Pasic 
5235cbab1bfSHalil Pasic #define KVM_S390_FLIC_CLASS(klass) \
5245cbab1bfSHalil Pasic     OBJECT_CLASS_CHECK(KVMS390FLICStateClass, (klass), TYPE_KVM_S390_FLIC)
5255cbab1bfSHalil Pasic 
5267b35d0c4SCornelia Huck static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
5277b35d0c4SCornelia Huck {
5287b35d0c4SCornelia Huck     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
5297b35d0c4SCornelia Huck     struct kvm_create_device cd = {0};
5309eccb862SHalil Pasic     struct kvm_device_attr test_attr = {0};
5317b35d0c4SCornelia Huck     int ret;
532f62f2109SHalil Pasic     Error *errp_local = NULL;
5337b35d0c4SCornelia Huck 
5345cbab1bfSHalil Pasic     KVM_S390_FLIC_GET_CLASS(dev)->parent_realize(dev, &errp_local);
5355cbab1bfSHalil Pasic     if (errp_local) {
5365cbab1bfSHalil Pasic         goto fail;
5375cbab1bfSHalil Pasic     }
5387b35d0c4SCornelia Huck     flic_state->fd = -1;
5397b35d0c4SCornelia Huck     if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
540f62f2109SHalil Pasic         error_setg_errno(&errp_local, errno, "KVM is missing capability"
541f62f2109SHalil Pasic                          " KVM_CAP_DEVICE_CTRL");
5427b35d0c4SCornelia Huck         trace_flic_no_device_api(errno);
543f62f2109SHalil Pasic         goto fail;
5447b35d0c4SCornelia Huck     }
5457b35d0c4SCornelia Huck 
5467b35d0c4SCornelia Huck     cd.type = KVM_DEV_TYPE_FLIC;
5477b35d0c4SCornelia Huck     ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
5487b35d0c4SCornelia Huck     if (ret < 0) {
549f62f2109SHalil Pasic         error_setg_errno(&errp_local, errno, "Creating the KVM device failed");
5507b35d0c4SCornelia Huck         trace_flic_create_device(errno);
551f62f2109SHalil Pasic         goto fail;
5527b35d0c4SCornelia Huck     }
5537b35d0c4SCornelia Huck     flic_state->fd = cd.fd;
5547b35d0c4SCornelia Huck 
5559eccb862SHalil Pasic     /* Check clear_io_irq support */
5569eccb862SHalil Pasic     test_attr.group = KVM_DEV_FLIC_CLEAR_IO_IRQ;
5579eccb862SHalil Pasic     flic_state->clear_io_supported = !ioctl(flic_state->fd,
5589eccb862SHalil Pasic                                             KVM_HAS_DEVICE_ATTR, test_attr);
559f62f2109SHalil Pasic     return;
560f62f2109SHalil Pasic fail:
561f62f2109SHalil Pasic     error_propagate(errp, errp_local);
5627b35d0c4SCornelia Huck }
5637b35d0c4SCornelia Huck 
5647b35d0c4SCornelia Huck static void kvm_s390_flic_reset(DeviceState *dev)
5657b35d0c4SCornelia Huck {
5667b35d0c4SCornelia Huck     KVMS390FLICState *flic = KVM_S390_FLIC(dev);
5676c1dd652SFei Li     S390FLICState *fs = S390_FLIC_COMMON(dev);
5687b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
5697b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_CLEAR_IRQS,
5707b35d0c4SCornelia Huck     };
5717b35d0c4SCornelia Huck     int rc = 0;
5726c1dd652SFei Li     uint8_t isc;
5737b35d0c4SCornelia Huck 
5747b35d0c4SCornelia Huck     if (flic->fd == -1) {
5757b35d0c4SCornelia Huck         return;
5767b35d0c4SCornelia Huck     }
5777b35d0c4SCornelia Huck 
5787b35d0c4SCornelia Huck     flic_disable_wait_pfault(flic);
5797b35d0c4SCornelia Huck 
5806c1dd652SFei Li     if (fs->ais_supported) {
5816c1dd652SFei Li         for (isc = 0; isc <= MAX_ISC; isc++) {
5826c1dd652SFei Li             rc = kvm_s390_modify_ais_mode(fs, isc, SIC_IRQ_MODE_ALL);
5836c1dd652SFei Li             if (rc) {
5846c1dd652SFei Li                 error_report("Failed to reset ais mode for isc %d: %s",
5856c1dd652SFei Li                              isc, strerror(-rc));
5866c1dd652SFei Li             }
5876c1dd652SFei Li         }
5886c1dd652SFei Li     }
5896c1dd652SFei Li 
5907b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
5917b35d0c4SCornelia Huck     if (rc) {
5927b35d0c4SCornelia Huck         trace_flic_reset_failed(errno);
5937b35d0c4SCornelia Huck     }
5947b35d0c4SCornelia Huck 
5957b35d0c4SCornelia Huck     flic_enable_pfault(flic);
5967b35d0c4SCornelia Huck }
5977b35d0c4SCornelia Huck 
5987b35d0c4SCornelia Huck static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
5997b35d0c4SCornelia Huck {
6007b35d0c4SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(oc);
60103cf077aSCornelia Huck     S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
6027b35d0c4SCornelia Huck 
6035cbab1bfSHalil Pasic     KVM_S390_FLIC_CLASS(oc)->parent_realize = dc->realize;
6047b35d0c4SCornelia Huck     dc->realize = kvm_s390_flic_realize;
605f2cab7f1SCornelia Huck     dc->vmsd = &kvm_s390_flic_vmstate;
6067b35d0c4SCornelia Huck     dc->reset = kvm_s390_flic_reset;
60703cf077aSCornelia Huck     fsc->register_io_adapter = kvm_s390_register_io_adapter;
608d426d9fbSCornelia Huck     fsc->io_adapter_map = kvm_s390_io_adapter_map;
609d426d9fbSCornelia Huck     fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
610d426d9fbSCornelia Huck     fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
6119eccb862SHalil Pasic     fsc->clear_io_irq = kvm_s390_clear_io_flic;
6126c1dd652SFei Li     fsc->modify_ais_mode = kvm_s390_modify_ais_mode;
6131622ffd5SYi Min Zhao     fsc->inject_airq = kvm_s390_inject_airq;
6147b35d0c4SCornelia Huck }
6157b35d0c4SCornelia Huck 
6167b35d0c4SCornelia Huck static const TypeInfo kvm_s390_flic_info = {
6177b35d0c4SCornelia Huck     .name          = TYPE_KVM_S390_FLIC,
6187b35d0c4SCornelia Huck     .parent        = TYPE_S390_FLIC_COMMON,
6197b35d0c4SCornelia Huck     .instance_size = sizeof(KVMS390FLICState),
6205cbab1bfSHalil Pasic     .class_size    = sizeof(KVMS390FLICStateClass),
6217b35d0c4SCornelia Huck     .class_init    = kvm_s390_flic_class_init,
6227b35d0c4SCornelia Huck };
6237b35d0c4SCornelia Huck 
6247b35d0c4SCornelia Huck static void kvm_s390_flic_register_types(void)
6257b35d0c4SCornelia Huck {
6267b35d0c4SCornelia Huck     type_register_static(&kvm_s390_flic_info);
6277b35d0c4SCornelia Huck }
6287b35d0c4SCornelia Huck 
6297b35d0c4SCornelia Huck type_init(kvm_s390_flic_register_types)
630