xref: /qemu/hw/intc/s390_flic_kvm.c (revision d426d9fba8ea5d728038a9bea6a7c51f11941157)
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 
137b35d0c4SCornelia Huck #include <sys/ioctl.h>
147b35d0c4SCornelia Huck #include "qemu/error-report.h"
157b35d0c4SCornelia Huck #include "hw/sysbus.h"
167b35d0c4SCornelia Huck #include "sysemu/kvm.h"
177b35d0c4SCornelia Huck #include "migration/qemu-file.h"
187b35d0c4SCornelia Huck #include "hw/s390x/s390_flic.h"
19*d426d9fbSCornelia Huck #include "hw/s390x/adapter.h"
207b35d0c4SCornelia Huck #include "trace.h"
217b35d0c4SCornelia Huck 
227b35d0c4SCornelia Huck #define FLIC_SAVE_INITIAL_SIZE getpagesize()
237b35d0c4SCornelia Huck #define FLIC_FAILED (-1UL)
247b35d0c4SCornelia Huck #define FLIC_SAVEVM_VERSION 1
257b35d0c4SCornelia Huck 
267b35d0c4SCornelia Huck typedef struct KVMS390FLICState {
277b35d0c4SCornelia Huck     S390FLICState parent_obj;
287b35d0c4SCornelia Huck 
297b35d0c4SCornelia Huck     uint32_t fd;
307b35d0c4SCornelia Huck } KVMS390FLICState;
317b35d0c4SCornelia Huck 
327b35d0c4SCornelia Huck DeviceState *s390_flic_kvm_create(void)
337b35d0c4SCornelia Huck {
347b35d0c4SCornelia Huck     DeviceState *dev = NULL;
357b35d0c4SCornelia Huck 
367b35d0c4SCornelia Huck     if (kvm_enabled()) {
377b35d0c4SCornelia Huck         dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
387b35d0c4SCornelia Huck         object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
397b35d0c4SCornelia Huck                                   OBJECT(dev), NULL);
407b35d0c4SCornelia Huck     }
417b35d0c4SCornelia Huck     return dev;
427b35d0c4SCornelia Huck }
437b35d0c4SCornelia Huck 
447b35d0c4SCornelia Huck /**
457b35d0c4SCornelia Huck  * flic_get_all_irqs - store all pending irqs in buffer
467b35d0c4SCornelia Huck  * @buf: pointer to buffer which is passed to kernel
477b35d0c4SCornelia Huck  * @len: length of buffer
487b35d0c4SCornelia Huck  * @flic: pointer to flic device state
497b35d0c4SCornelia Huck  *
507b35d0c4SCornelia Huck  * Returns: -ENOMEM if buffer is too small,
517b35d0c4SCornelia Huck  * -EINVAL if attr.group is invalid,
527b35d0c4SCornelia Huck  * -EFAULT if copying to userspace failed,
537b35d0c4SCornelia Huck  * on success return number of stored interrupts
547b35d0c4SCornelia Huck  */
557b35d0c4SCornelia Huck static int flic_get_all_irqs(KVMS390FLICState *flic,
567b35d0c4SCornelia Huck                              void *buf, int len)
577b35d0c4SCornelia Huck {
587b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
597b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_GET_ALL_IRQS,
607b35d0c4SCornelia Huck         .addr = (uint64_t) buf,
617b35d0c4SCornelia Huck         .attr = len,
627b35d0c4SCornelia Huck     };
637b35d0c4SCornelia Huck     int rc;
647b35d0c4SCornelia Huck 
657b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
667b35d0c4SCornelia Huck 
677b35d0c4SCornelia Huck     return rc == -1 ? -errno : rc;
687b35d0c4SCornelia Huck }
697b35d0c4SCornelia Huck 
707b35d0c4SCornelia Huck static void flic_enable_pfault(KVMS390FLICState *flic)
717b35d0c4SCornelia Huck {
727b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
737b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_APF_ENABLE,
747b35d0c4SCornelia Huck     };
757b35d0c4SCornelia Huck     int rc;
767b35d0c4SCornelia Huck 
777b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
787b35d0c4SCornelia Huck 
797b35d0c4SCornelia Huck     if (rc) {
807b35d0c4SCornelia Huck         fprintf(stderr, "flic: couldn't enable pfault\n");
817b35d0c4SCornelia Huck     }
827b35d0c4SCornelia Huck }
837b35d0c4SCornelia Huck 
847b35d0c4SCornelia Huck static void flic_disable_wait_pfault(KVMS390FLICState *flic)
857b35d0c4SCornelia Huck {
867b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
877b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
887b35d0c4SCornelia Huck     };
897b35d0c4SCornelia Huck     int rc;
907b35d0c4SCornelia Huck 
917b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
927b35d0c4SCornelia Huck 
937b35d0c4SCornelia Huck     if (rc) {
947b35d0c4SCornelia Huck         fprintf(stderr, "flic: couldn't disable pfault\n");
957b35d0c4SCornelia Huck     }
967b35d0c4SCornelia Huck }
977b35d0c4SCornelia Huck 
987b35d0c4SCornelia Huck /** flic_enqueue_irqs - returns 0 on success
997b35d0c4SCornelia Huck  * @buf: pointer to buffer which is passed to kernel
1007b35d0c4SCornelia Huck  * @len: length of buffer
1017b35d0c4SCornelia Huck  * @flic: pointer to flic device state
1027b35d0c4SCornelia Huck  *
1037b35d0c4SCornelia Huck  * Returns: -EINVAL if attr.group is unknown
1047b35d0c4SCornelia Huck  */
1057b35d0c4SCornelia Huck static int flic_enqueue_irqs(void *buf, uint64_t len,
1067b35d0c4SCornelia Huck                             KVMS390FLICState *flic)
1077b35d0c4SCornelia Huck {
1087b35d0c4SCornelia Huck     int rc;
1097b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
1107b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_ENQUEUE,
1117b35d0c4SCornelia Huck         .addr = (uint64_t) buf,
1127b35d0c4SCornelia Huck         .attr = len,
1137b35d0c4SCornelia Huck     };
1147b35d0c4SCornelia Huck 
1157b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
1167b35d0c4SCornelia Huck 
1177b35d0c4SCornelia Huck     return rc ? -errno : 0;
1187b35d0c4SCornelia Huck }
1197b35d0c4SCornelia Huck 
1207b35d0c4SCornelia Huck /**
1217b35d0c4SCornelia Huck  * __get_all_irqs - store all pending irqs in buffer
1227b35d0c4SCornelia Huck  * @flic: pointer to flic device state
1237b35d0c4SCornelia Huck  * @buf: pointer to pointer to a buffer
1247b35d0c4SCornelia Huck  * @len: length of buffer
1257b35d0c4SCornelia Huck  *
1267b35d0c4SCornelia Huck  * Returns: return value of flic_get_all_irqs
1277b35d0c4SCornelia Huck  * Note: Retry and increase buffer size until flic_get_all_irqs
1287b35d0c4SCornelia Huck  * either returns a value >= 0 or a negative error code.
1297b35d0c4SCornelia Huck  * -ENOMEM is an exception, which means the buffer is too small
1307b35d0c4SCornelia Huck  * and we should try again. Other negative error codes can be
1317b35d0c4SCornelia Huck  * -EFAULT and -EINVAL which we ignore at this point
1327b35d0c4SCornelia Huck  */
1337b35d0c4SCornelia Huck static int __get_all_irqs(KVMS390FLICState *flic,
1347b35d0c4SCornelia Huck                           void **buf, int len)
1357b35d0c4SCornelia Huck {
1367b35d0c4SCornelia Huck     int r;
1377b35d0c4SCornelia Huck 
1387b35d0c4SCornelia Huck     do {
1397b35d0c4SCornelia Huck         /* returns -ENOMEM if buffer is too small and number
1407b35d0c4SCornelia Huck          * of queued interrupts on success */
1417b35d0c4SCornelia Huck         r = flic_get_all_irqs(flic, *buf, len);
1427b35d0c4SCornelia Huck         if (r >= 0) {
1437b35d0c4SCornelia Huck             break;
1447b35d0c4SCornelia Huck         }
1457b35d0c4SCornelia Huck         len *= 2;
1467b35d0c4SCornelia Huck         *buf = g_try_realloc(*buf, len);
1477b35d0c4SCornelia Huck         if (!buf) {
1487b35d0c4SCornelia Huck             return -ENOMEM;
1497b35d0c4SCornelia Huck         }
1507b35d0c4SCornelia Huck     } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
1517b35d0c4SCornelia Huck 
1527b35d0c4SCornelia Huck     return r;
1537b35d0c4SCornelia Huck }
1547b35d0c4SCornelia Huck 
15503cf077aSCornelia Huck static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
15603cf077aSCornelia Huck                                         uint8_t isc, bool swap,
15703cf077aSCornelia Huck                                         bool is_maskable)
15803cf077aSCornelia Huck {
15903cf077aSCornelia Huck     struct kvm_s390_io_adapter adapter = {
16003cf077aSCornelia Huck         .id = id,
16103cf077aSCornelia Huck         .isc = isc,
16203cf077aSCornelia Huck         .maskable = is_maskable,
16303cf077aSCornelia Huck         .swap = swap,
16403cf077aSCornelia Huck     };
16503cf077aSCornelia Huck     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
16603cf077aSCornelia Huck     int r, ret;
16703cf077aSCornelia Huck     struct kvm_device_attr attr = {
16803cf077aSCornelia Huck         .group = KVM_DEV_FLIC_ADAPTER_REGISTER,
16903cf077aSCornelia Huck         .addr = (uint64_t)&adapter,
17003cf077aSCornelia Huck     };
17103cf077aSCornelia Huck 
17203cf077aSCornelia Huck     if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
17303cf077aSCornelia Huck         return -ENOSYS;
17403cf077aSCornelia Huck     }
17503cf077aSCornelia Huck 
17603cf077aSCornelia Huck     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
17703cf077aSCornelia Huck 
17803cf077aSCornelia Huck     ret = r ? -errno : 0;
17903cf077aSCornelia Huck     return ret;
18003cf077aSCornelia Huck }
18103cf077aSCornelia Huck 
182*d426d9fbSCornelia Huck static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
183*d426d9fbSCornelia Huck                                    uint64_t map_addr, bool do_map)
184*d426d9fbSCornelia Huck {
185*d426d9fbSCornelia Huck     struct kvm_s390_io_adapter_req req = {
186*d426d9fbSCornelia Huck         .id = id,
187*d426d9fbSCornelia Huck         .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP,
188*d426d9fbSCornelia Huck         .addr = map_addr,
189*d426d9fbSCornelia Huck     };
190*d426d9fbSCornelia Huck     struct kvm_device_attr attr = {
191*d426d9fbSCornelia Huck         .group = KVM_DEV_FLIC_ADAPTER_MODIFY,
192*d426d9fbSCornelia Huck         .addr = (uint64_t)&req,
193*d426d9fbSCornelia Huck     };
194*d426d9fbSCornelia Huck     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
195*d426d9fbSCornelia Huck     int r;
196*d426d9fbSCornelia Huck 
197*d426d9fbSCornelia Huck     if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
198*d426d9fbSCornelia Huck         return -ENOSYS;
199*d426d9fbSCornelia Huck     }
200*d426d9fbSCornelia Huck 
201*d426d9fbSCornelia Huck     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
202*d426d9fbSCornelia Huck     return r ? -errno : 0;
203*d426d9fbSCornelia Huck }
204*d426d9fbSCornelia Huck 
205*d426d9fbSCornelia Huck static int kvm_s390_add_adapter_routes(S390FLICState *fs,
206*d426d9fbSCornelia Huck                                        AdapterRoutes *routes)
207*d426d9fbSCornelia Huck {
208*d426d9fbSCornelia Huck     int ret, i;
209*d426d9fbSCornelia Huck     uint64_t ind_offset = routes->adapter.ind_offset;
210*d426d9fbSCornelia Huck 
211*d426d9fbSCornelia Huck     for (i = 0; i < routes->num_routes; i++) {
212*d426d9fbSCornelia Huck         ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter);
213*d426d9fbSCornelia Huck         if (ret < 0) {
214*d426d9fbSCornelia Huck             goto out_undo;
215*d426d9fbSCornelia Huck         }
216*d426d9fbSCornelia Huck         routes->gsi[i] = ret;
217*d426d9fbSCornelia Huck         routes->adapter.ind_offset++;
218*d426d9fbSCornelia Huck     }
219*d426d9fbSCornelia Huck     /* Restore passed-in structure to original state. */
220*d426d9fbSCornelia Huck     routes->adapter.ind_offset = ind_offset;
221*d426d9fbSCornelia Huck     return 0;
222*d426d9fbSCornelia Huck out_undo:
223*d426d9fbSCornelia Huck     while (--i >= 0) {
224*d426d9fbSCornelia Huck         kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
225*d426d9fbSCornelia Huck         routes->gsi[i] = -1;
226*d426d9fbSCornelia Huck     }
227*d426d9fbSCornelia Huck     routes->adapter.ind_offset = ind_offset;
228*d426d9fbSCornelia Huck     return ret;
229*d426d9fbSCornelia Huck }
230*d426d9fbSCornelia Huck 
231*d426d9fbSCornelia Huck static void kvm_s390_release_adapter_routes(S390FLICState *fs,
232*d426d9fbSCornelia Huck                                             AdapterRoutes *routes)
233*d426d9fbSCornelia Huck {
234*d426d9fbSCornelia Huck     int i;
235*d426d9fbSCornelia Huck 
236*d426d9fbSCornelia Huck     for (i = 0; i < routes->num_routes; i++) {
237*d426d9fbSCornelia Huck         if (routes->gsi[i] >= 0) {
238*d426d9fbSCornelia Huck             kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
239*d426d9fbSCornelia Huck             routes->gsi[i] = -1;
240*d426d9fbSCornelia Huck         }
241*d426d9fbSCornelia Huck     }
242*d426d9fbSCornelia Huck }
243*d426d9fbSCornelia Huck 
2447b35d0c4SCornelia Huck /**
2457b35d0c4SCornelia Huck  * kvm_flic_save - Save pending floating interrupts
2467b35d0c4SCornelia Huck  * @f: QEMUFile containing migration state
2477b35d0c4SCornelia Huck  * @opaque: pointer to flic device state
2487b35d0c4SCornelia Huck  *
2497b35d0c4SCornelia Huck  * Note: Pass buf and len to kernel. Start with one page and
2507b35d0c4SCornelia Huck  * increase until buffer is sufficient or maxium size is
2517b35d0c4SCornelia Huck  * reached
2527b35d0c4SCornelia Huck  */
2537b35d0c4SCornelia Huck static void kvm_flic_save(QEMUFile *f, void *opaque)
2547b35d0c4SCornelia Huck {
2557b35d0c4SCornelia Huck     KVMS390FLICState *flic = opaque;
2567b35d0c4SCornelia Huck     int len = FLIC_SAVE_INITIAL_SIZE;
2577b35d0c4SCornelia Huck     void *buf;
2587b35d0c4SCornelia Huck     int count;
2597b35d0c4SCornelia Huck 
2607b35d0c4SCornelia Huck     flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
2617b35d0c4SCornelia Huck 
2627b35d0c4SCornelia Huck     buf = g_try_malloc0(len);
2637b35d0c4SCornelia Huck     if (!buf) {
2647b35d0c4SCornelia Huck         /* Storing FLIC_FAILED into the count field here will cause the
2657b35d0c4SCornelia Huck          * target system to fail when attempting to load irqs from the
2667b35d0c4SCornelia Huck          * migration state */
2677b35d0c4SCornelia Huck         error_report("flic: couldn't allocate memory");
2687b35d0c4SCornelia Huck         qemu_put_be64(f, FLIC_FAILED);
2697b35d0c4SCornelia Huck         return;
2707b35d0c4SCornelia Huck     }
2717b35d0c4SCornelia Huck 
2727b35d0c4SCornelia Huck     count = __get_all_irqs(flic, &buf, len);
2737b35d0c4SCornelia Huck     if (count < 0) {
2747b35d0c4SCornelia Huck         error_report("flic: couldn't retrieve irqs from kernel, rc %d",
2757b35d0c4SCornelia Huck                      count);
2767b35d0c4SCornelia Huck         /* Storing FLIC_FAILED into the count field here will cause the
2777b35d0c4SCornelia Huck          * target system to fail when attempting to load irqs from the
2787b35d0c4SCornelia Huck          * migration state */
2797b35d0c4SCornelia Huck         qemu_put_be64(f, FLIC_FAILED);
2807b35d0c4SCornelia Huck     } else {
2817b35d0c4SCornelia Huck         qemu_put_be64(f, count);
2827b35d0c4SCornelia Huck         qemu_put_buffer(f, (uint8_t *) buf,
2837b35d0c4SCornelia Huck                         count * sizeof(struct kvm_s390_irq));
2847b35d0c4SCornelia Huck     }
2857b35d0c4SCornelia Huck     g_free(buf);
2867b35d0c4SCornelia Huck }
2877b35d0c4SCornelia Huck 
2887b35d0c4SCornelia Huck /**
2897b35d0c4SCornelia Huck  * kvm_flic_load - Load pending floating interrupts
2907b35d0c4SCornelia Huck  * @f: QEMUFile containing migration state
2917b35d0c4SCornelia Huck  * @opaque: pointer to flic device state
2927b35d0c4SCornelia Huck  * @version_id: version id for migration
2937b35d0c4SCornelia Huck  *
2947b35d0c4SCornelia Huck  * Returns: value of flic_enqueue_irqs, -EINVAL on error
2957b35d0c4SCornelia Huck  * Note: Do nothing when no interrupts where stored
2967b35d0c4SCornelia Huck  * in QEMUFile
2977b35d0c4SCornelia Huck  */
2987b35d0c4SCornelia Huck static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
2997b35d0c4SCornelia Huck {
3007b35d0c4SCornelia Huck     uint64_t len = 0;
3017b35d0c4SCornelia Huck     uint64_t count = 0;
3027b35d0c4SCornelia Huck     void *buf = NULL;
3037b35d0c4SCornelia Huck     int r = 0;
3047b35d0c4SCornelia Huck 
3057b35d0c4SCornelia Huck     if (version_id != FLIC_SAVEVM_VERSION) {
3067b35d0c4SCornelia Huck         r = -EINVAL;
3077b35d0c4SCornelia Huck         goto out;
3087b35d0c4SCornelia Huck     }
3097b35d0c4SCornelia Huck 
3107b35d0c4SCornelia Huck     flic_enable_pfault((struct KVMS390FLICState *) opaque);
3117b35d0c4SCornelia Huck 
3127b35d0c4SCornelia Huck     count = qemu_get_be64(f);
3137b35d0c4SCornelia Huck     len = count * sizeof(struct kvm_s390_irq);
3147b35d0c4SCornelia Huck     if (count == FLIC_FAILED) {
3157b35d0c4SCornelia Huck         r = -EINVAL;
3167b35d0c4SCornelia Huck         goto out;
3177b35d0c4SCornelia Huck     }
3187b35d0c4SCornelia Huck     if (count == 0) {
3197b35d0c4SCornelia Huck         r = 0;
3207b35d0c4SCornelia Huck         goto out;
3217b35d0c4SCornelia Huck     }
3227b35d0c4SCornelia Huck     buf = g_try_malloc0(len);
3237b35d0c4SCornelia Huck     if (!buf) {
3247b35d0c4SCornelia Huck         r = -ENOMEM;
3257b35d0c4SCornelia Huck         goto out;
3267b35d0c4SCornelia Huck     }
3277b35d0c4SCornelia Huck 
3287b35d0c4SCornelia Huck     if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
3297b35d0c4SCornelia Huck         r = -EINVAL;
3307b35d0c4SCornelia Huck         goto out_free;
3317b35d0c4SCornelia Huck     }
3327b35d0c4SCornelia Huck     r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
3337b35d0c4SCornelia Huck 
3347b35d0c4SCornelia Huck out_free:
3357b35d0c4SCornelia Huck     g_free(buf);
3367b35d0c4SCornelia Huck out:
3377b35d0c4SCornelia Huck     return r;
3387b35d0c4SCornelia Huck }
3397b35d0c4SCornelia Huck 
3407b35d0c4SCornelia Huck static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
3417b35d0c4SCornelia Huck {
3427b35d0c4SCornelia Huck     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
3437b35d0c4SCornelia Huck     struct kvm_create_device cd = {0};
3447b35d0c4SCornelia Huck     int ret;
3457b35d0c4SCornelia Huck 
3467b35d0c4SCornelia Huck     flic_state->fd = -1;
3477b35d0c4SCornelia Huck     if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
3487b35d0c4SCornelia Huck         trace_flic_no_device_api(errno);
3497b35d0c4SCornelia Huck         return;
3507b35d0c4SCornelia Huck     }
3517b35d0c4SCornelia Huck 
3527b35d0c4SCornelia Huck     cd.type = KVM_DEV_TYPE_FLIC;
3537b35d0c4SCornelia Huck     ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
3547b35d0c4SCornelia Huck     if (ret < 0) {
3557b35d0c4SCornelia Huck         trace_flic_create_device(errno);
3567b35d0c4SCornelia Huck         return;
3577b35d0c4SCornelia Huck     }
3587b35d0c4SCornelia Huck     flic_state->fd = cd.fd;
3597b35d0c4SCornelia Huck 
3607b35d0c4SCornelia Huck     /* Register savevm handler for floating interrupts */
3617b35d0c4SCornelia Huck     register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
3627b35d0c4SCornelia Huck                     kvm_flic_load, (void *) flic_state);
3637b35d0c4SCornelia Huck }
3647b35d0c4SCornelia Huck 
3657b35d0c4SCornelia Huck static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
3667b35d0c4SCornelia Huck {
3677b35d0c4SCornelia Huck     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
3687b35d0c4SCornelia Huck 
3697b35d0c4SCornelia Huck     unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
3707b35d0c4SCornelia Huck }
3717b35d0c4SCornelia Huck 
3727b35d0c4SCornelia Huck static void kvm_s390_flic_reset(DeviceState *dev)
3737b35d0c4SCornelia Huck {
3747b35d0c4SCornelia Huck     KVMS390FLICState *flic = KVM_S390_FLIC(dev);
3757b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
3767b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_CLEAR_IRQS,
3777b35d0c4SCornelia Huck     };
3787b35d0c4SCornelia Huck     int rc = 0;
3797b35d0c4SCornelia Huck 
3807b35d0c4SCornelia Huck     if (flic->fd == -1) {
3817b35d0c4SCornelia Huck         return;
3827b35d0c4SCornelia Huck     }
3837b35d0c4SCornelia Huck 
3847b35d0c4SCornelia Huck     flic_disable_wait_pfault(flic);
3857b35d0c4SCornelia Huck 
3867b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
3877b35d0c4SCornelia Huck     if (rc) {
3887b35d0c4SCornelia Huck         trace_flic_reset_failed(errno);
3897b35d0c4SCornelia Huck     }
3907b35d0c4SCornelia Huck 
3917b35d0c4SCornelia Huck     flic_enable_pfault(flic);
3927b35d0c4SCornelia Huck }
3937b35d0c4SCornelia Huck 
3947b35d0c4SCornelia Huck static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
3957b35d0c4SCornelia Huck {
3967b35d0c4SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(oc);
39703cf077aSCornelia Huck     S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
3987b35d0c4SCornelia Huck 
3997b35d0c4SCornelia Huck     dc->realize = kvm_s390_flic_realize;
4007b35d0c4SCornelia Huck     dc->unrealize = kvm_s390_flic_unrealize;
4017b35d0c4SCornelia Huck     dc->reset = kvm_s390_flic_reset;
40203cf077aSCornelia Huck     fsc->register_io_adapter = kvm_s390_register_io_adapter;
403*d426d9fbSCornelia Huck     fsc->io_adapter_map = kvm_s390_io_adapter_map;
404*d426d9fbSCornelia Huck     fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
405*d426d9fbSCornelia Huck     fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
4067b35d0c4SCornelia Huck }
4077b35d0c4SCornelia Huck 
4087b35d0c4SCornelia Huck static const TypeInfo kvm_s390_flic_info = {
4097b35d0c4SCornelia Huck     .name          = TYPE_KVM_S390_FLIC,
4107b35d0c4SCornelia Huck     .parent        = TYPE_S390_FLIC_COMMON,
4117b35d0c4SCornelia Huck     .instance_size = sizeof(KVMS390FLICState),
4127b35d0c4SCornelia Huck     .class_init    = kvm_s390_flic_class_init,
4137b35d0c4SCornelia Huck };
4147b35d0c4SCornelia Huck 
4157b35d0c4SCornelia Huck static void kvm_s390_flic_register_types(void)
4167b35d0c4SCornelia Huck {
4177b35d0c4SCornelia Huck     type_register_static(&kvm_s390_flic_info);
4187b35d0c4SCornelia Huck }
4197b35d0c4SCornelia Huck 
4207b35d0c4SCornelia Huck type_init(kvm_s390_flic_register_types)
421