xref: /qemu/hw/intc/s390_flic_kvm.c (revision 1497c1606615b0b08d1b1f78afd1dcf2585879c0)
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"
237b35d0c4SCornelia Huck #include "trace.h"
247b35d0c4SCornelia Huck 
257b35d0c4SCornelia Huck #define FLIC_SAVE_INITIAL_SIZE getpagesize()
267b35d0c4SCornelia Huck #define FLIC_FAILED (-1UL)
277b35d0c4SCornelia Huck #define FLIC_SAVEVM_VERSION 1
287b35d0c4SCornelia Huck 
297b35d0c4SCornelia Huck typedef struct KVMS390FLICState {
307b35d0c4SCornelia Huck     S390FLICState parent_obj;
317b35d0c4SCornelia Huck 
327b35d0c4SCornelia Huck     uint32_t fd;
339eccb862SHalil Pasic     bool clear_io_supported;
347b35d0c4SCornelia Huck } KVMS390FLICState;
357b35d0c4SCornelia Huck 
367b35d0c4SCornelia Huck DeviceState *s390_flic_kvm_create(void)
377b35d0c4SCornelia Huck {
387b35d0c4SCornelia Huck     DeviceState *dev = NULL;
397b35d0c4SCornelia Huck 
407b35d0c4SCornelia Huck     if (kvm_enabled()) {
417b35d0c4SCornelia Huck         dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
427b35d0c4SCornelia Huck         object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
437b35d0c4SCornelia Huck                                   OBJECT(dev), NULL);
447b35d0c4SCornelia Huck     }
457b35d0c4SCornelia Huck     return dev;
467b35d0c4SCornelia Huck }
477b35d0c4SCornelia Huck 
487b35d0c4SCornelia Huck /**
497b35d0c4SCornelia Huck  * flic_get_all_irqs - store all pending irqs in buffer
507b35d0c4SCornelia Huck  * @buf: pointer to buffer which is passed to kernel
517b35d0c4SCornelia Huck  * @len: length of buffer
527b35d0c4SCornelia Huck  * @flic: pointer to flic device state
537b35d0c4SCornelia Huck  *
547b35d0c4SCornelia Huck  * Returns: -ENOMEM if buffer is too small,
557b35d0c4SCornelia Huck  * -EINVAL if attr.group is invalid,
567b35d0c4SCornelia Huck  * -EFAULT if copying to userspace failed,
577b35d0c4SCornelia Huck  * on success return number of stored interrupts
587b35d0c4SCornelia Huck  */
597b35d0c4SCornelia Huck static int flic_get_all_irqs(KVMS390FLICState *flic,
607b35d0c4SCornelia Huck                              void *buf, int len)
617b35d0c4SCornelia Huck {
627b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
637b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_GET_ALL_IRQS,
647b35d0c4SCornelia Huck         .addr = (uint64_t) buf,
657b35d0c4SCornelia Huck         .attr = len,
667b35d0c4SCornelia Huck     };
677b35d0c4SCornelia Huck     int rc;
687b35d0c4SCornelia Huck 
697b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
707b35d0c4SCornelia Huck 
717b35d0c4SCornelia Huck     return rc == -1 ? -errno : rc;
727b35d0c4SCornelia Huck }
737b35d0c4SCornelia Huck 
747b35d0c4SCornelia Huck static void flic_enable_pfault(KVMS390FLICState *flic)
757b35d0c4SCornelia Huck {
767b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
777b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_APF_ENABLE,
787b35d0c4SCornelia Huck     };
797b35d0c4SCornelia Huck     int rc;
807b35d0c4SCornelia Huck 
817b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
827b35d0c4SCornelia Huck 
837b35d0c4SCornelia Huck     if (rc) {
847b35d0c4SCornelia Huck         fprintf(stderr, "flic: couldn't enable pfault\n");
857b35d0c4SCornelia Huck     }
867b35d0c4SCornelia Huck }
877b35d0c4SCornelia Huck 
887b35d0c4SCornelia Huck static void flic_disable_wait_pfault(KVMS390FLICState *flic)
897b35d0c4SCornelia Huck {
907b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
917b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
927b35d0c4SCornelia Huck     };
937b35d0c4SCornelia Huck     int rc;
947b35d0c4SCornelia Huck 
957b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
967b35d0c4SCornelia Huck 
977b35d0c4SCornelia Huck     if (rc) {
987b35d0c4SCornelia Huck         fprintf(stderr, "flic: couldn't disable pfault\n");
997b35d0c4SCornelia Huck     }
1007b35d0c4SCornelia Huck }
1017b35d0c4SCornelia Huck 
1027b35d0c4SCornelia Huck /** flic_enqueue_irqs - returns 0 on success
1037b35d0c4SCornelia Huck  * @buf: pointer to buffer which is passed to kernel
1047b35d0c4SCornelia Huck  * @len: length of buffer
1057b35d0c4SCornelia Huck  * @flic: pointer to flic device state
1067b35d0c4SCornelia Huck  *
1077b35d0c4SCornelia Huck  * Returns: -EINVAL if attr.group is unknown
1087b35d0c4SCornelia Huck  */
1097b35d0c4SCornelia Huck static int flic_enqueue_irqs(void *buf, uint64_t len,
1107b35d0c4SCornelia Huck                             KVMS390FLICState *flic)
1117b35d0c4SCornelia Huck {
1127b35d0c4SCornelia Huck     int rc;
1137b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
1147b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_ENQUEUE,
1157b35d0c4SCornelia Huck         .addr = (uint64_t) buf,
1167b35d0c4SCornelia Huck         .attr = len,
1177b35d0c4SCornelia Huck     };
1187b35d0c4SCornelia Huck 
1197b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
1207b35d0c4SCornelia Huck 
1217b35d0c4SCornelia Huck     return rc ? -errno : 0;
1227b35d0c4SCornelia Huck }
1237b35d0c4SCornelia Huck 
124bbd8bb8eSCornelia Huck int kvm_s390_inject_flic(struct kvm_s390_irq *irq)
125bbd8bb8eSCornelia Huck {
126bbd8bb8eSCornelia Huck     static KVMS390FLICState *flic;
127bbd8bb8eSCornelia Huck 
128bbd8bb8eSCornelia Huck     if (unlikely(!flic)) {
129bbd8bb8eSCornelia Huck         flic = KVM_S390_FLIC(s390_get_flic());
130bbd8bb8eSCornelia Huck     }
131bbd8bb8eSCornelia Huck     return flic_enqueue_irqs(irq, sizeof(*irq), flic);
132bbd8bb8eSCornelia Huck }
133bbd8bb8eSCornelia Huck 
1349eccb862SHalil Pasic static int kvm_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id,
1359eccb862SHalil Pasic                            uint16_t subchannel_nr)
1369eccb862SHalil Pasic {
1379eccb862SHalil Pasic     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
1389eccb862SHalil Pasic     int rc;
1399eccb862SHalil Pasic     uint32_t sid = subchannel_id << 16 | subchannel_nr;
1409eccb862SHalil Pasic     struct kvm_device_attr attr = {
1419eccb862SHalil Pasic         .group = KVM_DEV_FLIC_CLEAR_IO_IRQ,
1429eccb862SHalil Pasic         .addr = (uint64_t) &sid,
1439eccb862SHalil Pasic         .attr = sizeof(sid),
1449eccb862SHalil Pasic     };
1459eccb862SHalil Pasic     if (unlikely(!flic->clear_io_supported)) {
1469eccb862SHalil Pasic         return -ENOSYS;
1479eccb862SHalil Pasic     }
1489eccb862SHalil Pasic     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
1499eccb862SHalil Pasic     return rc ? -errno : 0;
1509eccb862SHalil Pasic }
1519eccb862SHalil Pasic 
1527b35d0c4SCornelia Huck /**
1537b35d0c4SCornelia Huck  * __get_all_irqs - store all pending irqs in buffer
1547b35d0c4SCornelia Huck  * @flic: pointer to flic device state
1557b35d0c4SCornelia Huck  * @buf: pointer to pointer to a buffer
1567b35d0c4SCornelia Huck  * @len: length of buffer
1577b35d0c4SCornelia Huck  *
1587b35d0c4SCornelia Huck  * Returns: return value of flic_get_all_irqs
1597b35d0c4SCornelia Huck  * Note: Retry and increase buffer size until flic_get_all_irqs
1607b35d0c4SCornelia Huck  * either returns a value >= 0 or a negative error code.
1617b35d0c4SCornelia Huck  * -ENOMEM is an exception, which means the buffer is too small
1627b35d0c4SCornelia Huck  * and we should try again. Other negative error codes can be
1637b35d0c4SCornelia Huck  * -EFAULT and -EINVAL which we ignore at this point
1647b35d0c4SCornelia Huck  */
1657b35d0c4SCornelia Huck static int __get_all_irqs(KVMS390FLICState *flic,
1667b35d0c4SCornelia Huck                           void **buf, int len)
1677b35d0c4SCornelia Huck {
1687b35d0c4SCornelia Huck     int r;
1697b35d0c4SCornelia Huck 
1707b35d0c4SCornelia Huck     do {
1717b35d0c4SCornelia Huck         /* returns -ENOMEM if buffer is too small and number
1727b35d0c4SCornelia Huck          * of queued interrupts on success */
1737b35d0c4SCornelia Huck         r = flic_get_all_irqs(flic, *buf, len);
1747b35d0c4SCornelia Huck         if (r >= 0) {
1757b35d0c4SCornelia Huck             break;
1767b35d0c4SCornelia Huck         }
1777b35d0c4SCornelia Huck         len *= 2;
1787b35d0c4SCornelia Huck         *buf = g_try_realloc(*buf, len);
1797b35d0c4SCornelia Huck         if (!buf) {
1807b35d0c4SCornelia Huck             return -ENOMEM;
1817b35d0c4SCornelia Huck         }
1827b35d0c4SCornelia Huck     } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
1837b35d0c4SCornelia Huck 
1847b35d0c4SCornelia Huck     return r;
1857b35d0c4SCornelia Huck }
1867b35d0c4SCornelia Huck 
18703cf077aSCornelia Huck static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
18803cf077aSCornelia Huck                                         uint8_t isc, bool swap,
189*1497c160SFei Li                                         bool is_maskable, uint8_t flags)
19003cf077aSCornelia Huck {
19103cf077aSCornelia Huck     struct kvm_s390_io_adapter adapter = {
19203cf077aSCornelia Huck         .id = id,
19303cf077aSCornelia Huck         .isc = isc,
19403cf077aSCornelia Huck         .maskable = is_maskable,
19503cf077aSCornelia Huck         .swap = swap,
196*1497c160SFei Li         .flags = flags,
19703cf077aSCornelia Huck     };
19803cf077aSCornelia Huck     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
1999be38598SEduardo Habkost     int r;
20003cf077aSCornelia Huck     struct kvm_device_attr attr = {
20103cf077aSCornelia Huck         .group = KVM_DEV_FLIC_ADAPTER_REGISTER,
20203cf077aSCornelia Huck         .addr = (uint64_t)&adapter,
20303cf077aSCornelia Huck     };
20403cf077aSCornelia Huck 
2054cbd6c41SFei Li     if (!kvm_gsi_routing_enabled()) {
20608da527fSCornelia Huck         /* nothing to do */
20708da527fSCornelia Huck         return 0;
20803cf077aSCornelia Huck     }
20903cf077aSCornelia Huck 
21003cf077aSCornelia Huck     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
21103cf077aSCornelia Huck 
2129be38598SEduardo Habkost     return r ? -errno : 0;
21303cf077aSCornelia Huck }
21403cf077aSCornelia Huck 
215d426d9fbSCornelia Huck static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
216d426d9fbSCornelia Huck                                    uint64_t map_addr, bool do_map)
217d426d9fbSCornelia Huck {
218d426d9fbSCornelia Huck     struct kvm_s390_io_adapter_req req = {
219d426d9fbSCornelia Huck         .id = id,
220d426d9fbSCornelia Huck         .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP,
221d426d9fbSCornelia Huck         .addr = map_addr,
222d426d9fbSCornelia Huck     };
223d426d9fbSCornelia Huck     struct kvm_device_attr attr = {
224d426d9fbSCornelia Huck         .group = KVM_DEV_FLIC_ADAPTER_MODIFY,
225d426d9fbSCornelia Huck         .addr = (uint64_t)&req,
226d426d9fbSCornelia Huck     };
227d426d9fbSCornelia Huck     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
228d426d9fbSCornelia Huck     int r;
229d426d9fbSCornelia Huck 
2304cbd6c41SFei Li     if (!kvm_gsi_routing_enabled()) {
23108da527fSCornelia Huck         /* nothing to do */
23208da527fSCornelia Huck         return 0;
233d426d9fbSCornelia Huck     }
234d426d9fbSCornelia Huck 
235d426d9fbSCornelia Huck     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
236d426d9fbSCornelia Huck     return r ? -errno : 0;
237d426d9fbSCornelia Huck }
238d426d9fbSCornelia Huck 
239d426d9fbSCornelia Huck static int kvm_s390_add_adapter_routes(S390FLICState *fs,
240d426d9fbSCornelia Huck                                        AdapterRoutes *routes)
241d426d9fbSCornelia Huck {
242d426d9fbSCornelia Huck     int ret, i;
243d426d9fbSCornelia Huck     uint64_t ind_offset = routes->adapter.ind_offset;
244d426d9fbSCornelia Huck 
245d426d9fbSCornelia Huck     for (i = 0; i < routes->num_routes; i++) {
246d426d9fbSCornelia Huck         ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter);
247d426d9fbSCornelia Huck         if (ret < 0) {
248d426d9fbSCornelia Huck             goto out_undo;
249d426d9fbSCornelia Huck         }
250d426d9fbSCornelia Huck         routes->gsi[i] = ret;
251d426d9fbSCornelia Huck         routes->adapter.ind_offset++;
252d426d9fbSCornelia Huck     }
253c0194a00SJens Freimann     kvm_irqchip_commit_routes(kvm_state);
254c0194a00SJens Freimann 
255d426d9fbSCornelia Huck     /* Restore passed-in structure to original state. */
256d426d9fbSCornelia Huck     routes->adapter.ind_offset = ind_offset;
257d426d9fbSCornelia Huck     return 0;
258d426d9fbSCornelia Huck out_undo:
259d426d9fbSCornelia Huck     while (--i >= 0) {
260d426d9fbSCornelia Huck         kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
261d426d9fbSCornelia Huck         routes->gsi[i] = -1;
262d426d9fbSCornelia Huck     }
263d426d9fbSCornelia Huck     routes->adapter.ind_offset = ind_offset;
264d426d9fbSCornelia Huck     return ret;
265d426d9fbSCornelia Huck }
266d426d9fbSCornelia Huck 
267d426d9fbSCornelia Huck static void kvm_s390_release_adapter_routes(S390FLICState *fs,
268d426d9fbSCornelia Huck                                             AdapterRoutes *routes)
269d426d9fbSCornelia Huck {
270d426d9fbSCornelia Huck     int i;
271d426d9fbSCornelia Huck 
272d426d9fbSCornelia Huck     for (i = 0; i < routes->num_routes; i++) {
273d426d9fbSCornelia Huck         if (routes->gsi[i] >= 0) {
274d426d9fbSCornelia Huck             kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
275d426d9fbSCornelia Huck             routes->gsi[i] = -1;
276d426d9fbSCornelia Huck         }
277d426d9fbSCornelia Huck     }
278d426d9fbSCornelia Huck }
279d426d9fbSCornelia Huck 
2807b35d0c4SCornelia Huck /**
2817b35d0c4SCornelia Huck  * kvm_flic_save - Save pending floating interrupts
2827b35d0c4SCornelia Huck  * @f: QEMUFile containing migration state
2837b35d0c4SCornelia Huck  * @opaque: pointer to flic device state
284f2cab7f1SCornelia Huck  * @size: ignored
2857b35d0c4SCornelia Huck  *
2867b35d0c4SCornelia Huck  * Note: Pass buf and len to kernel. Start with one page and
2877b35d0c4SCornelia Huck  * increase until buffer is sufficient or maxium size is
2887b35d0c4SCornelia Huck  * reached
2897b35d0c4SCornelia Huck  */
2902c21ee76SJianjun Duan static int kvm_flic_save(QEMUFile *f, void *opaque, size_t size,
2912c21ee76SJianjun Duan                          VMStateField *field, QJSON *vmdesc)
2927b35d0c4SCornelia Huck {
2937b35d0c4SCornelia Huck     KVMS390FLICState *flic = opaque;
2947b35d0c4SCornelia Huck     int len = FLIC_SAVE_INITIAL_SIZE;
2957b35d0c4SCornelia Huck     void *buf;
2967b35d0c4SCornelia Huck     int count;
297ba690c71SCornelia Huck     int r = 0;
2987b35d0c4SCornelia Huck 
2997b35d0c4SCornelia Huck     flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
3007b35d0c4SCornelia Huck 
3017b35d0c4SCornelia Huck     buf = g_try_malloc0(len);
3027b35d0c4SCornelia Huck     if (!buf) {
3037b35d0c4SCornelia Huck         /* Storing FLIC_FAILED into the count field here will cause the
3047b35d0c4SCornelia Huck          * target system to fail when attempting to load irqs from the
3057b35d0c4SCornelia Huck          * migration state */
3067b35d0c4SCornelia Huck         error_report("flic: couldn't allocate memory");
3077b35d0c4SCornelia Huck         qemu_put_be64(f, FLIC_FAILED);
308ba690c71SCornelia Huck         return -ENOMEM;
3097b35d0c4SCornelia Huck     }
3107b35d0c4SCornelia Huck 
3117b35d0c4SCornelia Huck     count = __get_all_irqs(flic, &buf, len);
3127b35d0c4SCornelia Huck     if (count < 0) {
3137b35d0c4SCornelia Huck         error_report("flic: couldn't retrieve irqs from kernel, rc %d",
3147b35d0c4SCornelia Huck                      count);
3157b35d0c4SCornelia Huck         /* Storing FLIC_FAILED into the count field here will cause the
3167b35d0c4SCornelia Huck          * target system to fail when attempting to load irqs from the
3177b35d0c4SCornelia Huck          * migration state */
3187b35d0c4SCornelia Huck         qemu_put_be64(f, FLIC_FAILED);
319ba690c71SCornelia Huck         r = count;
3207b35d0c4SCornelia Huck     } else {
3217b35d0c4SCornelia Huck         qemu_put_be64(f, count);
3227b35d0c4SCornelia Huck         qemu_put_buffer(f, (uint8_t *) buf,
3237b35d0c4SCornelia Huck                         count * sizeof(struct kvm_s390_irq));
3247b35d0c4SCornelia Huck     }
3257b35d0c4SCornelia Huck     g_free(buf);
3262c21ee76SJianjun Duan 
327ba690c71SCornelia Huck     return r;
3287b35d0c4SCornelia Huck }
3297b35d0c4SCornelia Huck 
3307b35d0c4SCornelia Huck /**
3317b35d0c4SCornelia Huck  * kvm_flic_load - Load pending floating interrupts
3327b35d0c4SCornelia Huck  * @f: QEMUFile containing migration state
3337b35d0c4SCornelia Huck  * @opaque: pointer to flic device state
334f2cab7f1SCornelia Huck  * @size: ignored
3357b35d0c4SCornelia Huck  *
3367b35d0c4SCornelia Huck  * Returns: value of flic_enqueue_irqs, -EINVAL on error
3377b35d0c4SCornelia Huck  * Note: Do nothing when no interrupts where stored
3387b35d0c4SCornelia Huck  * in QEMUFile
3397b35d0c4SCornelia Huck  */
3402c21ee76SJianjun Duan static int kvm_flic_load(QEMUFile *f, void *opaque, size_t size,
3412c21ee76SJianjun Duan                          VMStateField *field)
3427b35d0c4SCornelia Huck {
3437b35d0c4SCornelia Huck     uint64_t len = 0;
3447b35d0c4SCornelia Huck     uint64_t count = 0;
3457b35d0c4SCornelia Huck     void *buf = NULL;
3467b35d0c4SCornelia Huck     int r = 0;
3477b35d0c4SCornelia Huck 
3487b35d0c4SCornelia Huck     flic_enable_pfault((struct KVMS390FLICState *) opaque);
3497b35d0c4SCornelia Huck 
3507b35d0c4SCornelia Huck     count = qemu_get_be64(f);
3517b35d0c4SCornelia Huck     len = count * sizeof(struct kvm_s390_irq);
3527b35d0c4SCornelia Huck     if (count == FLIC_FAILED) {
3537b35d0c4SCornelia Huck         r = -EINVAL;
3547b35d0c4SCornelia Huck         goto out;
3557b35d0c4SCornelia Huck     }
3567b35d0c4SCornelia Huck     if (count == 0) {
3577b35d0c4SCornelia Huck         r = 0;
3587b35d0c4SCornelia Huck         goto out;
3597b35d0c4SCornelia Huck     }
3607b35d0c4SCornelia Huck     buf = g_try_malloc0(len);
3617b35d0c4SCornelia Huck     if (!buf) {
3627b35d0c4SCornelia Huck         r = -ENOMEM;
3637b35d0c4SCornelia Huck         goto out;
3647b35d0c4SCornelia Huck     }
3657b35d0c4SCornelia Huck 
3667b35d0c4SCornelia Huck     if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
3677b35d0c4SCornelia Huck         r = -EINVAL;
3687b35d0c4SCornelia Huck         goto out_free;
3697b35d0c4SCornelia Huck     }
3707b35d0c4SCornelia Huck     r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
3717b35d0c4SCornelia Huck 
3727b35d0c4SCornelia Huck out_free:
3737b35d0c4SCornelia Huck     g_free(buf);
3747b35d0c4SCornelia Huck out:
3757b35d0c4SCornelia Huck     return r;
3767b35d0c4SCornelia Huck }
3777b35d0c4SCornelia Huck 
378f2cab7f1SCornelia Huck static const VMStateDescription kvm_s390_flic_vmstate = {
379f2cab7f1SCornelia Huck     .name = "s390-flic",
380f2cab7f1SCornelia Huck     .version_id = FLIC_SAVEVM_VERSION,
381f2cab7f1SCornelia Huck     .minimum_version_id = FLIC_SAVEVM_VERSION,
382f2cab7f1SCornelia Huck     .fields = (VMStateField[]) {
383f2cab7f1SCornelia Huck         {
384f2cab7f1SCornelia Huck             .name = "irqs",
385f2cab7f1SCornelia Huck             .info = &(const VMStateInfo) {
386f2cab7f1SCornelia Huck                 .name = "irqs",
387f2cab7f1SCornelia Huck                 .get = kvm_flic_load,
388f2cab7f1SCornelia Huck                 .put = kvm_flic_save,
389f2cab7f1SCornelia Huck             },
390f2cab7f1SCornelia Huck             .flags = VMS_SINGLE,
391f2cab7f1SCornelia Huck         },
392f2cab7f1SCornelia Huck         VMSTATE_END_OF_LIST()
393f2cab7f1SCornelia Huck     }
394f2cab7f1SCornelia Huck };
395f2cab7f1SCornelia Huck 
3965cbab1bfSHalil Pasic typedef struct KVMS390FLICStateClass {
3975cbab1bfSHalil Pasic     S390FLICStateClass parent_class;
3985cbab1bfSHalil Pasic     DeviceRealize parent_realize;
3995cbab1bfSHalil Pasic } KVMS390FLICStateClass;
4005cbab1bfSHalil Pasic 
4015cbab1bfSHalil Pasic #define KVM_S390_FLIC_GET_CLASS(obj) \
4025cbab1bfSHalil Pasic     OBJECT_GET_CLASS(KVMS390FLICStateClass, (obj), TYPE_KVM_S390_FLIC)
4035cbab1bfSHalil Pasic 
4045cbab1bfSHalil Pasic #define KVM_S390_FLIC_CLASS(klass) \
4055cbab1bfSHalil Pasic     OBJECT_CLASS_CHECK(KVMS390FLICStateClass, (klass), TYPE_KVM_S390_FLIC)
4065cbab1bfSHalil Pasic 
4077b35d0c4SCornelia Huck static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
4087b35d0c4SCornelia Huck {
4097b35d0c4SCornelia Huck     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
4107b35d0c4SCornelia Huck     struct kvm_create_device cd = {0};
4119eccb862SHalil Pasic     struct kvm_device_attr test_attr = {0};
4127b35d0c4SCornelia Huck     int ret;
413f62f2109SHalil Pasic     Error *errp_local = NULL;
4147b35d0c4SCornelia Huck 
4155cbab1bfSHalil Pasic     KVM_S390_FLIC_GET_CLASS(dev)->parent_realize(dev, &errp_local);
4165cbab1bfSHalil Pasic     if (errp_local) {
4175cbab1bfSHalil Pasic         goto fail;
4185cbab1bfSHalil Pasic     }
4197b35d0c4SCornelia Huck     flic_state->fd = -1;
4207b35d0c4SCornelia Huck     if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
421f62f2109SHalil Pasic         error_setg_errno(&errp_local, errno, "KVM is missing capability"
422f62f2109SHalil Pasic                          " KVM_CAP_DEVICE_CTRL");
4237b35d0c4SCornelia Huck         trace_flic_no_device_api(errno);
424f62f2109SHalil Pasic         goto fail;
4257b35d0c4SCornelia Huck     }
4267b35d0c4SCornelia Huck 
4277b35d0c4SCornelia Huck     cd.type = KVM_DEV_TYPE_FLIC;
4287b35d0c4SCornelia Huck     ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
4297b35d0c4SCornelia Huck     if (ret < 0) {
430f62f2109SHalil Pasic         error_setg_errno(&errp_local, errno, "Creating the KVM device failed");
4317b35d0c4SCornelia Huck         trace_flic_create_device(errno);
432f62f2109SHalil Pasic         goto fail;
4337b35d0c4SCornelia Huck     }
4347b35d0c4SCornelia Huck     flic_state->fd = cd.fd;
4357b35d0c4SCornelia Huck 
4369eccb862SHalil Pasic     /* Check clear_io_irq support */
4379eccb862SHalil Pasic     test_attr.group = KVM_DEV_FLIC_CLEAR_IO_IRQ;
4389eccb862SHalil Pasic     flic_state->clear_io_supported = !ioctl(flic_state->fd,
4399eccb862SHalil Pasic                                             KVM_HAS_DEVICE_ATTR, test_attr);
4409eccb862SHalil Pasic 
441f62f2109SHalil Pasic     return;
442f62f2109SHalil Pasic fail:
443f62f2109SHalil Pasic     error_propagate(errp, errp_local);
4447b35d0c4SCornelia Huck }
4457b35d0c4SCornelia Huck 
4467b35d0c4SCornelia Huck static void kvm_s390_flic_reset(DeviceState *dev)
4477b35d0c4SCornelia Huck {
4487b35d0c4SCornelia Huck     KVMS390FLICState *flic = KVM_S390_FLIC(dev);
4497b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
4507b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_CLEAR_IRQS,
4517b35d0c4SCornelia Huck     };
4527b35d0c4SCornelia Huck     int rc = 0;
4537b35d0c4SCornelia Huck 
4547b35d0c4SCornelia Huck     if (flic->fd == -1) {
4557b35d0c4SCornelia Huck         return;
4567b35d0c4SCornelia Huck     }
4577b35d0c4SCornelia Huck 
4587b35d0c4SCornelia Huck     flic_disable_wait_pfault(flic);
4597b35d0c4SCornelia Huck 
4607b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
4617b35d0c4SCornelia Huck     if (rc) {
4627b35d0c4SCornelia Huck         trace_flic_reset_failed(errno);
4637b35d0c4SCornelia Huck     }
4647b35d0c4SCornelia Huck 
4657b35d0c4SCornelia Huck     flic_enable_pfault(flic);
4667b35d0c4SCornelia Huck }
4677b35d0c4SCornelia Huck 
4687b35d0c4SCornelia Huck static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
4697b35d0c4SCornelia Huck {
4707b35d0c4SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(oc);
47103cf077aSCornelia Huck     S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
4727b35d0c4SCornelia Huck 
4735cbab1bfSHalil Pasic     KVM_S390_FLIC_CLASS(oc)->parent_realize = dc->realize;
4747b35d0c4SCornelia Huck     dc->realize = kvm_s390_flic_realize;
475f2cab7f1SCornelia Huck     dc->vmsd = &kvm_s390_flic_vmstate;
4767b35d0c4SCornelia Huck     dc->reset = kvm_s390_flic_reset;
47703cf077aSCornelia Huck     fsc->register_io_adapter = kvm_s390_register_io_adapter;
478d426d9fbSCornelia Huck     fsc->io_adapter_map = kvm_s390_io_adapter_map;
479d426d9fbSCornelia Huck     fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
480d426d9fbSCornelia Huck     fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
4819eccb862SHalil Pasic     fsc->clear_io_irq = kvm_s390_clear_io_flic;
4827b35d0c4SCornelia Huck }
4837b35d0c4SCornelia Huck 
4847b35d0c4SCornelia Huck static const TypeInfo kvm_s390_flic_info = {
4857b35d0c4SCornelia Huck     .name          = TYPE_KVM_S390_FLIC,
4867b35d0c4SCornelia Huck     .parent        = TYPE_S390_FLIC_COMMON,
4877b35d0c4SCornelia Huck     .instance_size = sizeof(KVMS390FLICState),
4885cbab1bfSHalil Pasic     .class_size    = sizeof(KVMS390FLICStateClass),
4897b35d0c4SCornelia Huck     .class_init    = kvm_s390_flic_class_init,
4907b35d0c4SCornelia Huck };
4917b35d0c4SCornelia Huck 
4927b35d0c4SCornelia Huck static void kvm_s390_flic_register_types(void)
4937b35d0c4SCornelia Huck {
4947b35d0c4SCornelia Huck     type_register_static(&kvm_s390_flic_info);
4957b35d0c4SCornelia Huck }
4967b35d0c4SCornelia Huck 
4977b35d0c4SCornelia Huck type_init(kvm_s390_flic_register_types)
498