xref: /qemu/hw/intc/s390_flic_kvm.c (revision 3ddba9a9e9bedd20a0b60dcdbe86f16223555555)
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 "cpu.h"
15f16bbb9bSDavid Hildenbrand #include "kvm_s390x.h"
167b35d0c4SCornelia Huck #include <sys/ioctl.h>
177b35d0c4SCornelia Huck #include "qemu/error-report.h"
180b8fa32fSMarkus Armbruster #include "qemu/module.h"
19f62f2109SHalil Pasic #include "qapi/error.h"
207b35d0c4SCornelia Huck #include "hw/sysbus.h"
217b35d0c4SCornelia Huck #include "sysemu/kvm.h"
227b35d0c4SCornelia Huck #include "hw/s390x/s390_flic.h"
23d426d9fbSCornelia Huck #include "hw/s390x/adapter.h"
246c1dd652SFei Li #include "hw/s390x/css.h"
25ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h"
267b35d0c4SCornelia Huck #include "trace.h"
27db1015e9SEduardo Habkost #include "qom/object.h"
287b35d0c4SCornelia Huck 
29038adc2fSWei Yang #define FLIC_SAVE_INITIAL_SIZE qemu_real_host_page_size
307b35d0c4SCornelia Huck #define FLIC_FAILED (-1UL)
317b35d0c4SCornelia Huck #define FLIC_SAVEVM_VERSION 1
327b35d0c4SCornelia Huck 
33b13f9bdfSEduardo Habkost struct KVMS390FLICState{
347b35d0c4SCornelia Huck     S390FLICState parent_obj;
357b35d0c4SCornelia Huck 
367b35d0c4SCornelia Huck     uint32_t fd;
379eccb862SHalil Pasic     bool clear_io_supported;
38b13f9bdfSEduardo Habkost };
397b35d0c4SCornelia Huck 
40c21a6106SDavid Hildenbrand static KVMS390FLICState *s390_get_kvm_flic(S390FLICState *fs)
41c21a6106SDavid Hildenbrand {
42c21a6106SDavid Hildenbrand     static KVMS390FLICState *flic;
43c21a6106SDavid Hildenbrand 
44c21a6106SDavid Hildenbrand     if (!flic) {
45c21a6106SDavid Hildenbrand         /* we only have one flic device, so this is fine to cache */
46c21a6106SDavid Hildenbrand         flic = KVM_S390_FLIC(fs);
47c21a6106SDavid Hildenbrand     }
48c21a6106SDavid Hildenbrand     return flic;
49c21a6106SDavid Hildenbrand }
50c21a6106SDavid Hildenbrand 
517b35d0c4SCornelia Huck /**
527b35d0c4SCornelia Huck  * flic_get_all_irqs - store all pending irqs in buffer
537b35d0c4SCornelia Huck  * @buf: pointer to buffer which is passed to kernel
547b35d0c4SCornelia Huck  * @len: length of buffer
557b35d0c4SCornelia Huck  * @flic: pointer to flic device state
567b35d0c4SCornelia Huck  *
577b35d0c4SCornelia Huck  * Returns: -ENOMEM if buffer is too small,
587b35d0c4SCornelia Huck  * -EINVAL if attr.group is invalid,
597b35d0c4SCornelia Huck  * -EFAULT if copying to userspace failed,
607b35d0c4SCornelia Huck  * on success return number of stored interrupts
617b35d0c4SCornelia Huck  */
627b35d0c4SCornelia Huck static int flic_get_all_irqs(KVMS390FLICState *flic,
637b35d0c4SCornelia Huck                              void *buf, int len)
647b35d0c4SCornelia Huck {
657b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
667b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_GET_ALL_IRQS,
677b35d0c4SCornelia Huck         .addr = (uint64_t) buf,
687b35d0c4SCornelia Huck         .attr = len,
697b35d0c4SCornelia Huck     };
707b35d0c4SCornelia Huck     int rc;
717b35d0c4SCornelia Huck 
727b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
737b35d0c4SCornelia Huck 
747b35d0c4SCornelia Huck     return rc == -1 ? -errno : rc;
757b35d0c4SCornelia Huck }
767b35d0c4SCornelia Huck 
777b35d0c4SCornelia Huck static void flic_enable_pfault(KVMS390FLICState *flic)
787b35d0c4SCornelia Huck {
797b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
807b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_APF_ENABLE,
817b35d0c4SCornelia Huck     };
827b35d0c4SCornelia Huck     int rc;
837b35d0c4SCornelia Huck 
847b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
857b35d0c4SCornelia Huck 
867b35d0c4SCornelia Huck     if (rc) {
877b35d0c4SCornelia Huck         fprintf(stderr, "flic: couldn't enable pfault\n");
887b35d0c4SCornelia Huck     }
897b35d0c4SCornelia Huck }
907b35d0c4SCornelia Huck 
917b35d0c4SCornelia Huck static void flic_disable_wait_pfault(KVMS390FLICState *flic)
927b35d0c4SCornelia Huck {
937b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
947b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
957b35d0c4SCornelia Huck     };
967b35d0c4SCornelia Huck     int rc;
977b35d0c4SCornelia Huck 
987b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
997b35d0c4SCornelia Huck 
1007b35d0c4SCornelia Huck     if (rc) {
1017b35d0c4SCornelia Huck         fprintf(stderr, "flic: couldn't disable pfault\n");
1027b35d0c4SCornelia Huck     }
1037b35d0c4SCornelia Huck }
1047b35d0c4SCornelia Huck 
1057b35d0c4SCornelia Huck /** flic_enqueue_irqs - returns 0 on success
1067b35d0c4SCornelia Huck  * @buf: pointer to buffer which is passed to kernel
1077b35d0c4SCornelia Huck  * @len: length of buffer
1087b35d0c4SCornelia Huck  * @flic: pointer to flic device state
1097b35d0c4SCornelia Huck  *
1107b35d0c4SCornelia Huck  * Returns: -EINVAL if attr.group is unknown
1117b35d0c4SCornelia Huck  */
1127b35d0c4SCornelia Huck static int flic_enqueue_irqs(void *buf, uint64_t len,
1137b35d0c4SCornelia Huck                             KVMS390FLICState *flic)
1147b35d0c4SCornelia Huck {
1157b35d0c4SCornelia Huck     int rc;
1167b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
1177b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_ENQUEUE,
1187b35d0c4SCornelia Huck         .addr = (uint64_t) buf,
1197b35d0c4SCornelia Huck         .attr = len,
1207b35d0c4SCornelia Huck     };
1217b35d0c4SCornelia Huck 
1227b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
1237b35d0c4SCornelia Huck 
1247b35d0c4SCornelia Huck     return rc ? -errno : 0;
1257b35d0c4SCornelia Huck }
1267b35d0c4SCornelia Huck 
127e6505d53SDavid Hildenbrand static void kvm_s390_inject_flic(S390FLICState *fs, struct kvm_s390_irq *irq)
128bbd8bb8eSCornelia Huck {
129e6505d53SDavid Hildenbrand     static bool use_flic = true;
130e6505d53SDavid Hildenbrand     int r;
131bbd8bb8eSCornelia Huck 
132e6505d53SDavid Hildenbrand     if (use_flic) {
133c21a6106SDavid Hildenbrand         r = flic_enqueue_irqs(irq, sizeof(*irq), s390_get_kvm_flic(fs));
134e6505d53SDavid Hildenbrand         if (r == -ENOSYS) {
135e6505d53SDavid Hildenbrand             use_flic = false;
136bbd8bb8eSCornelia Huck         }
137e6505d53SDavid Hildenbrand         if (!r) {
138e6505d53SDavid Hildenbrand             return;
139e6505d53SDavid Hildenbrand         }
140e6505d53SDavid Hildenbrand     }
141e6505d53SDavid Hildenbrand     /* fallback to legacy KVM IOCTL in case FLIC fails */
142e6505d53SDavid Hildenbrand     kvm_s390_floating_interrupt_legacy(irq);
143e6505d53SDavid Hildenbrand }
144e6505d53SDavid Hildenbrand 
145e6505d53SDavid Hildenbrand static void kvm_s390_inject_service(S390FLICState *fs, uint32_t parm)
146e6505d53SDavid Hildenbrand {
147e6505d53SDavid Hildenbrand         struct kvm_s390_irq irq = {
148e6505d53SDavid Hildenbrand         .type = KVM_S390_INT_SERVICE,
149e6505d53SDavid Hildenbrand         .u.ext.ext_params = parm,
150e6505d53SDavid Hildenbrand     };
151e6505d53SDavid Hildenbrand 
152e6505d53SDavid Hildenbrand     kvm_s390_inject_flic(fs, &irq);
153e6505d53SDavid Hildenbrand }
154e6505d53SDavid Hildenbrand 
155e6505d53SDavid Hildenbrand static void kvm_s390_inject_io(S390FLICState *fs, uint16_t subchannel_id,
156e6505d53SDavid Hildenbrand                                uint16_t subchannel_nr, uint32_t io_int_parm,
157e6505d53SDavid Hildenbrand                                uint32_t io_int_word)
158e6505d53SDavid Hildenbrand {
159e6505d53SDavid Hildenbrand     struct kvm_s390_irq irq = {
160e6505d53SDavid Hildenbrand         .u.io.subchannel_id = subchannel_id,
161e6505d53SDavid Hildenbrand         .u.io.subchannel_nr = subchannel_nr,
162e6505d53SDavid Hildenbrand         .u.io.io_int_parm = io_int_parm,
163e6505d53SDavid Hildenbrand         .u.io.io_int_word = io_int_word,
164e6505d53SDavid Hildenbrand     };
165e6505d53SDavid Hildenbrand 
166e6505d53SDavid Hildenbrand     if (io_int_word & IO_INT_WORD_AI) {
167e6505d53SDavid Hildenbrand         irq.type = KVM_S390_INT_IO(1, 0, 0, 0);
168e6505d53SDavid Hildenbrand     } else {
169e6505d53SDavid Hildenbrand         irq.type = KVM_S390_INT_IO(0, (subchannel_id & 0xff00) >> 8,
170e6505d53SDavid Hildenbrand                                       (subchannel_id & 0x0006),
171e6505d53SDavid Hildenbrand                                       subchannel_nr);
172e6505d53SDavid Hildenbrand     }
173e6505d53SDavid Hildenbrand     kvm_s390_inject_flic(fs, &irq);
174e6505d53SDavid Hildenbrand }
175e6505d53SDavid Hildenbrand 
176e6505d53SDavid Hildenbrand static void kvm_s390_inject_crw_mchk(S390FLICState *fs)
177e6505d53SDavid Hildenbrand {
178e6505d53SDavid Hildenbrand     struct kvm_s390_irq irq = {
179e6505d53SDavid Hildenbrand         .type = KVM_S390_MCHK,
180e6505d53SDavid Hildenbrand         .u.mchk.cr14 = CR14_CHANNEL_REPORT_SC,
181e6505d53SDavid Hildenbrand         .u.mchk.mcic = s390_build_validity_mcic() | MCIC_SC_CP,
182e6505d53SDavid Hildenbrand     };
183e6505d53SDavid Hildenbrand 
184e6505d53SDavid Hildenbrand     kvm_s390_inject_flic(fs, &irq);
185bbd8bb8eSCornelia Huck }
186bbd8bb8eSCornelia Huck 
1879eccb862SHalil Pasic static int kvm_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id,
1889eccb862SHalil Pasic                            uint16_t subchannel_nr)
1899eccb862SHalil Pasic {
190c21a6106SDavid Hildenbrand     KVMS390FLICState *flic = s390_get_kvm_flic(fs);
1919eccb862SHalil Pasic     int rc;
1929eccb862SHalil Pasic     uint32_t sid = subchannel_id << 16 | subchannel_nr;
1939eccb862SHalil Pasic     struct kvm_device_attr attr = {
1949eccb862SHalil Pasic         .group = KVM_DEV_FLIC_CLEAR_IO_IRQ,
1959eccb862SHalil Pasic         .addr = (uint64_t) &sid,
1969eccb862SHalil Pasic         .attr = sizeof(sid),
1979eccb862SHalil Pasic     };
1989eccb862SHalil Pasic     if (unlikely(!flic->clear_io_supported)) {
1999eccb862SHalil Pasic         return -ENOSYS;
2009eccb862SHalil Pasic     }
2019eccb862SHalil Pasic     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
2029eccb862SHalil Pasic     return rc ? -errno : 0;
2039eccb862SHalil Pasic }
2049eccb862SHalil Pasic 
2056c1dd652SFei Li static int kvm_s390_modify_ais_mode(S390FLICState *fs, uint8_t isc,
2066c1dd652SFei Li                                     uint16_t mode)
2076c1dd652SFei Li {
208c21a6106SDavid Hildenbrand     KVMS390FLICState *flic = s390_get_kvm_flic(fs);
2096c1dd652SFei Li     struct kvm_s390_ais_req req = {
2106c1dd652SFei Li         .isc = isc,
2116c1dd652SFei Li         .mode = mode,
2126c1dd652SFei Li     };
2136c1dd652SFei Li     struct kvm_device_attr attr = {
2146c1dd652SFei Li         .group = KVM_DEV_FLIC_AISM,
2156c1dd652SFei Li         .addr = (uint64_t)&req,
2166c1dd652SFei Li     };
2176c1dd652SFei Li 
2186c1dd652SFei Li     if (!fs->ais_supported) {
2196c1dd652SFei Li         return -ENOSYS;
2206c1dd652SFei Li     }
2216c1dd652SFei Li 
2226c1dd652SFei Li     return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0;
2236c1dd652SFei Li }
2246c1dd652SFei Li 
2251622ffd5SYi Min Zhao static int kvm_s390_inject_airq(S390FLICState *fs, uint8_t type,
2261622ffd5SYi Min Zhao                                 uint8_t isc, uint8_t flags)
2271622ffd5SYi Min Zhao {
228c21a6106SDavid Hildenbrand     KVMS390FLICState *flic = s390_get_kvm_flic(fs);
2291622ffd5SYi Min Zhao     uint32_t id = css_get_adapter_id(type, isc);
2301622ffd5SYi Min Zhao     struct kvm_device_attr attr = {
2311622ffd5SYi Min Zhao         .group = KVM_DEV_FLIC_AIRQ_INJECT,
2321622ffd5SYi Min Zhao         .attr = id,
2331622ffd5SYi Min Zhao     };
2341622ffd5SYi Min Zhao 
2351622ffd5SYi Min Zhao     if (!fs->ais_supported) {
2361622ffd5SYi Min Zhao         return -ENOSYS;
2371622ffd5SYi Min Zhao     }
2381622ffd5SYi Min Zhao 
2391622ffd5SYi Min Zhao     return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0;
2401622ffd5SYi Min Zhao }
2411622ffd5SYi Min Zhao 
2427b35d0c4SCornelia Huck /**
2437b35d0c4SCornelia Huck  * __get_all_irqs - store all pending irqs in buffer
2447b35d0c4SCornelia Huck  * @flic: pointer to flic device state
2457b35d0c4SCornelia Huck  * @buf: pointer to pointer to a buffer
2467b35d0c4SCornelia Huck  * @len: length of buffer
2477b35d0c4SCornelia Huck  *
2487b35d0c4SCornelia Huck  * Returns: return value of flic_get_all_irqs
2497b35d0c4SCornelia Huck  * Note: Retry and increase buffer size until flic_get_all_irqs
2507b35d0c4SCornelia Huck  * either returns a value >= 0 or a negative error code.
2517b35d0c4SCornelia Huck  * -ENOMEM is an exception, which means the buffer is too small
2527b35d0c4SCornelia Huck  * and we should try again. Other negative error codes can be
2537b35d0c4SCornelia Huck  * -EFAULT and -EINVAL which we ignore at this point
2547b35d0c4SCornelia Huck  */
2557b35d0c4SCornelia Huck static int __get_all_irqs(KVMS390FLICState *flic,
2567b35d0c4SCornelia Huck                           void **buf, int len)
2577b35d0c4SCornelia Huck {
2587b35d0c4SCornelia Huck     int r;
2597b35d0c4SCornelia Huck 
2607b35d0c4SCornelia Huck     do {
2617b35d0c4SCornelia Huck         /* returns -ENOMEM if buffer is too small and number
2627b35d0c4SCornelia Huck          * of queued interrupts on success */
2637b35d0c4SCornelia Huck         r = flic_get_all_irqs(flic, *buf, len);
2647b35d0c4SCornelia Huck         if (r >= 0) {
2657b35d0c4SCornelia Huck             break;
2667b35d0c4SCornelia Huck         }
2677b35d0c4SCornelia Huck         len *= 2;
2687b35d0c4SCornelia Huck         *buf = g_try_realloc(*buf, len);
2697b35d0c4SCornelia Huck         if (!buf) {
2707b35d0c4SCornelia Huck             return -ENOMEM;
2717b35d0c4SCornelia Huck         }
2727b35d0c4SCornelia Huck     } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
2737b35d0c4SCornelia Huck 
2747b35d0c4SCornelia Huck     return r;
2757b35d0c4SCornelia Huck }
2767b35d0c4SCornelia Huck 
27703cf077aSCornelia Huck static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
27803cf077aSCornelia Huck                                         uint8_t isc, bool swap,
2791497c160SFei Li                                         bool is_maskable, uint8_t flags)
28003cf077aSCornelia Huck {
28103cf077aSCornelia Huck     struct kvm_s390_io_adapter adapter = {
28203cf077aSCornelia Huck         .id = id,
28303cf077aSCornelia Huck         .isc = isc,
28403cf077aSCornelia Huck         .maskable = is_maskable,
28503cf077aSCornelia Huck         .swap = swap,
2861497c160SFei Li         .flags = flags,
28703cf077aSCornelia Huck     };
28803cf077aSCornelia Huck     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
2899be38598SEduardo Habkost     int r;
29003cf077aSCornelia Huck     struct kvm_device_attr attr = {
29103cf077aSCornelia Huck         .group = KVM_DEV_FLIC_ADAPTER_REGISTER,
29203cf077aSCornelia Huck         .addr = (uint64_t)&adapter,
29303cf077aSCornelia Huck     };
29403cf077aSCornelia Huck 
2954cbd6c41SFei Li     if (!kvm_gsi_routing_enabled()) {
29608da527fSCornelia Huck         /* nothing to do */
29708da527fSCornelia Huck         return 0;
29803cf077aSCornelia Huck     }
29903cf077aSCornelia Huck 
30003cf077aSCornelia Huck     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
30103cf077aSCornelia Huck 
3029be38598SEduardo Habkost     return r ? -errno : 0;
30303cf077aSCornelia Huck }
30403cf077aSCornelia Huck 
305d426d9fbSCornelia Huck static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
306d426d9fbSCornelia Huck                                    uint64_t map_addr, bool do_map)
307d426d9fbSCornelia Huck {
308d426d9fbSCornelia Huck     struct kvm_s390_io_adapter_req req = {
309d426d9fbSCornelia Huck         .id = id,
310d426d9fbSCornelia Huck         .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP,
311d426d9fbSCornelia Huck         .addr = map_addr,
312d426d9fbSCornelia Huck     };
313d426d9fbSCornelia Huck     struct kvm_device_attr attr = {
314d426d9fbSCornelia Huck         .group = KVM_DEV_FLIC_ADAPTER_MODIFY,
315d426d9fbSCornelia Huck         .addr = (uint64_t)&req,
316d426d9fbSCornelia Huck     };
317c21a6106SDavid Hildenbrand     KVMS390FLICState *flic = s390_get_kvm_flic(fs);
318d426d9fbSCornelia Huck     int r;
319d426d9fbSCornelia Huck 
3204cbd6c41SFei Li     if (!kvm_gsi_routing_enabled()) {
32108da527fSCornelia Huck         /* nothing to do */
32208da527fSCornelia Huck         return 0;
323d426d9fbSCornelia Huck     }
324d426d9fbSCornelia Huck 
325d426d9fbSCornelia Huck     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
326d426d9fbSCornelia Huck     return r ? -errno : 0;
327d426d9fbSCornelia Huck }
328d426d9fbSCornelia Huck 
329d426d9fbSCornelia Huck static int kvm_s390_add_adapter_routes(S390FLICState *fs,
330d426d9fbSCornelia Huck                                        AdapterRoutes *routes)
331d426d9fbSCornelia Huck {
332d426d9fbSCornelia Huck     int ret, i;
333d426d9fbSCornelia Huck     uint64_t ind_offset = routes->adapter.ind_offset;
334d426d9fbSCornelia Huck 
3353c5fd807SCornelia Huck     if (!kvm_gsi_routing_enabled()) {
3363c5fd807SCornelia Huck         return -ENOSYS;
3373c5fd807SCornelia Huck     }
3383c5fd807SCornelia Huck 
339d426d9fbSCornelia Huck     for (i = 0; i < routes->num_routes; i++) {
340d426d9fbSCornelia Huck         ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter);
341d426d9fbSCornelia Huck         if (ret < 0) {
342d426d9fbSCornelia Huck             goto out_undo;
343d426d9fbSCornelia Huck         }
344d426d9fbSCornelia Huck         routes->gsi[i] = ret;
345d426d9fbSCornelia Huck         routes->adapter.ind_offset++;
346d426d9fbSCornelia Huck     }
347c0194a00SJens Freimann     kvm_irqchip_commit_routes(kvm_state);
348c0194a00SJens Freimann 
349d426d9fbSCornelia Huck     /* Restore passed-in structure to original state. */
350d426d9fbSCornelia Huck     routes->adapter.ind_offset = ind_offset;
351d426d9fbSCornelia Huck     return 0;
352d426d9fbSCornelia Huck out_undo:
353d426d9fbSCornelia Huck     while (--i >= 0) {
354d426d9fbSCornelia Huck         kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
355d426d9fbSCornelia Huck         routes->gsi[i] = -1;
356d426d9fbSCornelia Huck     }
357d426d9fbSCornelia Huck     routes->adapter.ind_offset = ind_offset;
358d426d9fbSCornelia Huck     return ret;
359d426d9fbSCornelia Huck }
360d426d9fbSCornelia Huck 
361d426d9fbSCornelia Huck static void kvm_s390_release_adapter_routes(S390FLICState *fs,
362d426d9fbSCornelia Huck                                             AdapterRoutes *routes)
363d426d9fbSCornelia Huck {
364d426d9fbSCornelia Huck     int i;
365d426d9fbSCornelia Huck 
3663c5fd807SCornelia Huck     if (!kvm_gsi_routing_enabled()) {
3673c5fd807SCornelia Huck         return;
3683c5fd807SCornelia Huck     }
3693c5fd807SCornelia Huck 
370d426d9fbSCornelia Huck     for (i = 0; i < routes->num_routes; i++) {
371d426d9fbSCornelia Huck         if (routes->gsi[i] >= 0) {
372d426d9fbSCornelia Huck             kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
373d426d9fbSCornelia Huck             routes->gsi[i] = -1;
374d426d9fbSCornelia Huck         }
375d426d9fbSCornelia Huck     }
376d426d9fbSCornelia Huck }
377d426d9fbSCornelia Huck 
3787b35d0c4SCornelia Huck /**
3797b35d0c4SCornelia Huck  * kvm_flic_save - Save pending floating interrupts
3807b35d0c4SCornelia Huck  * @f: QEMUFile containing migration state
3817b35d0c4SCornelia Huck  * @opaque: pointer to flic device state
382f2cab7f1SCornelia Huck  * @size: ignored
3837b35d0c4SCornelia Huck  *
3847b35d0c4SCornelia Huck  * Note: Pass buf and len to kernel. Start with one page and
3857b35d0c4SCornelia Huck  * increase until buffer is sufficient or maxium size is
3867b35d0c4SCornelia Huck  * reached
3877b35d0c4SCornelia Huck  */
3882c21ee76SJianjun Duan static int kvm_flic_save(QEMUFile *f, void *opaque, size_t size,
389*3ddba9a9SMarkus Armbruster                          const VMStateField *field, JSONWriter *vmdesc)
3907b35d0c4SCornelia Huck {
3917b35d0c4SCornelia Huck     KVMS390FLICState *flic = opaque;
3927b35d0c4SCornelia Huck     int len = FLIC_SAVE_INITIAL_SIZE;
3937b35d0c4SCornelia Huck     void *buf;
3947b35d0c4SCornelia Huck     int count;
395ba690c71SCornelia Huck     int r = 0;
3967b35d0c4SCornelia Huck 
3977b35d0c4SCornelia Huck     flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
3987b35d0c4SCornelia Huck 
3997b35d0c4SCornelia Huck     buf = g_try_malloc0(len);
4007b35d0c4SCornelia Huck     if (!buf) {
4017b35d0c4SCornelia Huck         /* Storing FLIC_FAILED into the count field here will cause the
4027b35d0c4SCornelia Huck          * target system to fail when attempting to load irqs from the
4037b35d0c4SCornelia Huck          * migration state */
4047b35d0c4SCornelia Huck         error_report("flic: couldn't allocate memory");
4057b35d0c4SCornelia Huck         qemu_put_be64(f, FLIC_FAILED);
406ba690c71SCornelia Huck         return -ENOMEM;
4077b35d0c4SCornelia Huck     }
4087b35d0c4SCornelia Huck 
4097b35d0c4SCornelia Huck     count = __get_all_irqs(flic, &buf, len);
4107b35d0c4SCornelia Huck     if (count < 0) {
4117b35d0c4SCornelia Huck         error_report("flic: couldn't retrieve irqs from kernel, rc %d",
4127b35d0c4SCornelia Huck                      count);
4137b35d0c4SCornelia Huck         /* Storing FLIC_FAILED into the count field here will cause the
4147b35d0c4SCornelia Huck          * target system to fail when attempting to load irqs from the
4157b35d0c4SCornelia Huck          * migration state */
4167b35d0c4SCornelia Huck         qemu_put_be64(f, FLIC_FAILED);
417ba690c71SCornelia Huck         r = count;
4187b35d0c4SCornelia Huck     } else {
4197b35d0c4SCornelia Huck         qemu_put_be64(f, count);
4207b35d0c4SCornelia Huck         qemu_put_buffer(f, (uint8_t *) buf,
4217b35d0c4SCornelia Huck                         count * sizeof(struct kvm_s390_irq));
4227b35d0c4SCornelia Huck     }
4237b35d0c4SCornelia Huck     g_free(buf);
4242c21ee76SJianjun Duan 
425ba690c71SCornelia Huck     return r;
4267b35d0c4SCornelia Huck }
4277b35d0c4SCornelia Huck 
4287b35d0c4SCornelia Huck /**
4297b35d0c4SCornelia Huck  * kvm_flic_load - Load pending floating interrupts
4307b35d0c4SCornelia Huck  * @f: QEMUFile containing migration state
4317b35d0c4SCornelia Huck  * @opaque: pointer to flic device state
432f2cab7f1SCornelia Huck  * @size: ignored
4337b35d0c4SCornelia Huck  *
4347b35d0c4SCornelia Huck  * Returns: value of flic_enqueue_irqs, -EINVAL on error
4357b35d0c4SCornelia Huck  * Note: Do nothing when no interrupts where stored
4367b35d0c4SCornelia Huck  * in QEMUFile
4377b35d0c4SCornelia Huck  */
4382c21ee76SJianjun Duan static int kvm_flic_load(QEMUFile *f, void *opaque, size_t size,
43903fee66fSMarc-André Lureau                          const VMStateField *field)
4407b35d0c4SCornelia Huck {
4417b35d0c4SCornelia Huck     uint64_t len = 0;
4427b35d0c4SCornelia Huck     uint64_t count = 0;
4437b35d0c4SCornelia Huck     void *buf = NULL;
4447b35d0c4SCornelia Huck     int r = 0;
4457b35d0c4SCornelia Huck 
4467b35d0c4SCornelia Huck     flic_enable_pfault((struct KVMS390FLICState *) opaque);
4477b35d0c4SCornelia Huck 
4487b35d0c4SCornelia Huck     count = qemu_get_be64(f);
4497b35d0c4SCornelia Huck     len = count * sizeof(struct kvm_s390_irq);
4507b35d0c4SCornelia Huck     if (count == FLIC_FAILED) {
45165569bbfSDaniel Henrique Barboza         return -EINVAL;
4527b35d0c4SCornelia Huck     }
4537b35d0c4SCornelia Huck     if (count == 0) {
45465569bbfSDaniel Henrique Barboza         return 0;
4557b35d0c4SCornelia Huck     }
4567b35d0c4SCornelia Huck     buf = g_try_malloc0(len);
4577b35d0c4SCornelia Huck     if (!buf) {
45865569bbfSDaniel Henrique Barboza         return -ENOMEM;
4597b35d0c4SCornelia Huck     }
4607b35d0c4SCornelia Huck 
4617b35d0c4SCornelia Huck     if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
4627b35d0c4SCornelia Huck         r = -EINVAL;
4637b35d0c4SCornelia Huck         goto out_free;
4647b35d0c4SCornelia Huck     }
4657b35d0c4SCornelia Huck     r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
4667b35d0c4SCornelia Huck 
4677b35d0c4SCornelia Huck out_free:
4687b35d0c4SCornelia Huck     g_free(buf);
4697b35d0c4SCornelia Huck     return r;
4707b35d0c4SCornelia Huck }
4717b35d0c4SCornelia Huck 
472e7be8d49SYi Min Zhao typedef struct KVMS390FLICStateMigTmp {
473e7be8d49SYi Min Zhao     KVMS390FLICState *parent;
474e7be8d49SYi Min Zhao     uint8_t simm;
475e7be8d49SYi Min Zhao     uint8_t nimm;
476e7be8d49SYi Min Zhao } KVMS390FLICStateMigTmp;
477e7be8d49SYi Min Zhao 
47844b1ff31SDr. David Alan Gilbert static int kvm_flic_ais_pre_save(void *opaque)
479e7be8d49SYi Min Zhao {
480e7be8d49SYi Min Zhao     KVMS390FLICStateMigTmp *tmp = opaque;
481e7be8d49SYi Min Zhao     KVMS390FLICState *flic = tmp->parent;
482e7be8d49SYi Min Zhao     struct kvm_s390_ais_all ais;
483e7be8d49SYi Min Zhao     struct kvm_device_attr attr = {
484e7be8d49SYi Min Zhao         .group = KVM_DEV_FLIC_AISM_ALL,
485e7be8d49SYi Min Zhao         .addr = (uint64_t)&ais,
486e7be8d49SYi Min Zhao         .attr = sizeof(ais),
487e7be8d49SYi Min Zhao     };
488e7be8d49SYi Min Zhao 
489e7be8d49SYi Min Zhao     if (ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr)) {
490e7be8d49SYi Min Zhao         error_report("Failed to retrieve kvm flic ais states");
49144b1ff31SDr. David Alan Gilbert         return -EINVAL;
492e7be8d49SYi Min Zhao     }
493e7be8d49SYi Min Zhao 
494e7be8d49SYi Min Zhao     tmp->simm = ais.simm;
495e7be8d49SYi Min Zhao     tmp->nimm = ais.nimm;
49644b1ff31SDr. David Alan Gilbert 
49744b1ff31SDr. David Alan Gilbert     return 0;
498e7be8d49SYi Min Zhao }
499e7be8d49SYi Min Zhao 
500e7be8d49SYi Min Zhao static int kvm_flic_ais_post_load(void *opaque, int version_id)
501e7be8d49SYi Min Zhao {
502e7be8d49SYi Min Zhao     KVMS390FLICStateMigTmp *tmp = opaque;
503e7be8d49SYi Min Zhao     KVMS390FLICState *flic = tmp->parent;
504e7be8d49SYi Min Zhao     struct kvm_s390_ais_all ais = {
505e7be8d49SYi Min Zhao         .simm = tmp->simm,
506e7be8d49SYi Min Zhao         .nimm = tmp->nimm,
507e7be8d49SYi Min Zhao     };
508e7be8d49SYi Min Zhao     struct kvm_device_attr attr = {
509e7be8d49SYi Min Zhao         .group = KVM_DEV_FLIC_AISM_ALL,
510e7be8d49SYi Min Zhao         .addr = (uint64_t)&ais,
511e7be8d49SYi Min Zhao     };
512e7be8d49SYi Min Zhao 
513e7be8d49SYi Min Zhao     /* This can happen when the user mis-configures its guests in an
514e7be8d49SYi Min Zhao      * incompatible fashion or without a CPU model. For example using
515e7be8d49SYi Min Zhao      * qemu with -cpu host (which is not migration safe) and do a
516e7be8d49SYi Min Zhao      * migration from a host that has AIS to a host that has no AIS.
517e7be8d49SYi Min Zhao      * In that case the target system will reject the migration here.
518e7be8d49SYi Min Zhao      */
519e7be8d49SYi Min Zhao     if (!ais_needed(flic)) {
520e7be8d49SYi Min Zhao         return -ENOSYS;
521e7be8d49SYi Min Zhao     }
522e7be8d49SYi Min Zhao 
523e7be8d49SYi Min Zhao     return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0;
524e7be8d49SYi Min Zhao }
525e7be8d49SYi Min Zhao 
526e7be8d49SYi Min Zhao static const VMStateDescription kvm_s390_flic_ais_tmp = {
527e7be8d49SYi Min Zhao     .name = "s390-flic-ais-tmp",
528e7be8d49SYi Min Zhao     .pre_save = kvm_flic_ais_pre_save,
529e7be8d49SYi Min Zhao     .post_load = kvm_flic_ais_post_load,
530e7be8d49SYi Min Zhao     .fields = (VMStateField[]) {
531e7be8d49SYi Min Zhao         VMSTATE_UINT8(simm, KVMS390FLICStateMigTmp),
532e7be8d49SYi Min Zhao         VMSTATE_UINT8(nimm, KVMS390FLICStateMigTmp),
533e7be8d49SYi Min Zhao         VMSTATE_END_OF_LIST()
534e7be8d49SYi Min Zhao     }
535e7be8d49SYi Min Zhao };
536e7be8d49SYi Min Zhao 
537e7be8d49SYi Min Zhao static const VMStateDescription kvm_s390_flic_vmstate_ais = {
538e7be8d49SYi Min Zhao     .name = "s390-flic/ais",
539e7be8d49SYi Min Zhao     .version_id = 1,
540e7be8d49SYi Min Zhao     .minimum_version_id = 1,
541e7be8d49SYi Min Zhao     .needed = ais_needed,
542e7be8d49SYi Min Zhao     .fields = (VMStateField[]) {
543e7be8d49SYi Min Zhao         VMSTATE_WITH_TMP(KVMS390FLICState, KVMS390FLICStateMigTmp,
544e7be8d49SYi Min Zhao                          kvm_s390_flic_ais_tmp),
545e7be8d49SYi Min Zhao         VMSTATE_END_OF_LIST()
546e7be8d49SYi Min Zhao     }
547e7be8d49SYi Min Zhao };
548e7be8d49SYi Min Zhao 
549f2cab7f1SCornelia Huck static const VMStateDescription kvm_s390_flic_vmstate = {
550e7be8d49SYi Min Zhao     /* should have been like kvm-s390-flic,
551e7be8d49SYi Min Zhao      * can't change without breaking compat */
552f2cab7f1SCornelia Huck     .name = "s390-flic",
553f2cab7f1SCornelia Huck     .version_id = FLIC_SAVEVM_VERSION,
554f2cab7f1SCornelia Huck     .minimum_version_id = FLIC_SAVEVM_VERSION,
555f2cab7f1SCornelia Huck     .fields = (VMStateField[]) {
556f2cab7f1SCornelia Huck         {
557f2cab7f1SCornelia Huck             .name = "irqs",
558f2cab7f1SCornelia Huck             .info = &(const VMStateInfo) {
559f2cab7f1SCornelia Huck                 .name = "irqs",
560f2cab7f1SCornelia Huck                 .get = kvm_flic_load,
561f2cab7f1SCornelia Huck                 .put = kvm_flic_save,
562f2cab7f1SCornelia Huck             },
563f2cab7f1SCornelia Huck             .flags = VMS_SINGLE,
564f2cab7f1SCornelia Huck         },
565f2cab7f1SCornelia Huck         VMSTATE_END_OF_LIST()
566e7be8d49SYi Min Zhao     },
567e7be8d49SYi Min Zhao     .subsections = (const VMStateDescription * []) {
568e7be8d49SYi Min Zhao         &kvm_s390_flic_vmstate_ais,
569e7be8d49SYi Min Zhao         NULL
570f2cab7f1SCornelia Huck     }
571f2cab7f1SCornelia Huck };
572f2cab7f1SCornelia Huck 
573db1015e9SEduardo Habkost struct KVMS390FLICStateClass {
5745cbab1bfSHalil Pasic     S390FLICStateClass parent_class;
5755cbab1bfSHalil Pasic     DeviceRealize parent_realize;
576db1015e9SEduardo Habkost };
577db1015e9SEduardo Habkost typedef struct KVMS390FLICStateClass KVMS390FLICStateClass;
5785cbab1bfSHalil Pasic 
5798110fa1dSEduardo Habkost DECLARE_CLASS_CHECKERS(KVMS390FLICStateClass, KVM_S390_FLIC,
5808110fa1dSEduardo Habkost                        TYPE_KVM_S390_FLIC)
5815cbab1bfSHalil Pasic 
5825cbab1bfSHalil Pasic 
5837b35d0c4SCornelia Huck static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
5847b35d0c4SCornelia Huck {
5857b35d0c4SCornelia Huck     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
5867b35d0c4SCornelia Huck     struct kvm_create_device cd = {0};
5879eccb862SHalil Pasic     struct kvm_device_attr test_attr = {0};
5887b35d0c4SCornelia Huck     int ret;
5898ca63ba8SMarkus Armbruster     Error *err = NULL;
5907b35d0c4SCornelia Huck 
5918ca63ba8SMarkus Armbruster     KVM_S390_FLIC_GET_CLASS(dev)->parent_realize(dev, &err);
5928ca63ba8SMarkus Armbruster     if (err) {
593d402c983SMarkus Armbruster         error_propagate(errp, err);
594d402c983SMarkus Armbruster         return;
5955cbab1bfSHalil Pasic     }
5967b35d0c4SCornelia Huck     flic_state->fd = -1;
5977b35d0c4SCornelia Huck 
5987b35d0c4SCornelia Huck     cd.type = KVM_DEV_TYPE_FLIC;
5997b35d0c4SCornelia Huck     ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
6007b35d0c4SCornelia Huck     if (ret < 0) {
601d402c983SMarkus Armbruster         error_setg_errno(errp, errno, "Creating the KVM device failed");
6027b35d0c4SCornelia Huck         trace_flic_create_device(errno);
603d402c983SMarkus Armbruster         return;
6047b35d0c4SCornelia Huck     }
6057b35d0c4SCornelia Huck     flic_state->fd = cd.fd;
6067b35d0c4SCornelia Huck 
6079eccb862SHalil Pasic     /* Check clear_io_irq support */
6089eccb862SHalil Pasic     test_attr.group = KVM_DEV_FLIC_CLEAR_IO_IRQ;
6099eccb862SHalil Pasic     flic_state->clear_io_supported = !ioctl(flic_state->fd,
6109eccb862SHalil Pasic                                             KVM_HAS_DEVICE_ATTR, test_attr);
6117b35d0c4SCornelia Huck }
6127b35d0c4SCornelia Huck 
6137b35d0c4SCornelia Huck static void kvm_s390_flic_reset(DeviceState *dev)
6147b35d0c4SCornelia Huck {
6157b35d0c4SCornelia Huck     KVMS390FLICState *flic = KVM_S390_FLIC(dev);
6166c1dd652SFei Li     S390FLICState *fs = S390_FLIC_COMMON(dev);
6177b35d0c4SCornelia Huck     struct kvm_device_attr attr = {
6187b35d0c4SCornelia Huck         .group = KVM_DEV_FLIC_CLEAR_IRQS,
6197b35d0c4SCornelia Huck     };
6207b35d0c4SCornelia Huck     int rc = 0;
6216c1dd652SFei Li     uint8_t isc;
6227b35d0c4SCornelia Huck 
6237b35d0c4SCornelia Huck     if (flic->fd == -1) {
6247b35d0c4SCornelia Huck         return;
6257b35d0c4SCornelia Huck     }
6267b35d0c4SCornelia Huck 
6277b35d0c4SCornelia Huck     flic_disable_wait_pfault(flic);
6287b35d0c4SCornelia Huck 
6296c1dd652SFei Li     if (fs->ais_supported) {
6306c1dd652SFei Li         for (isc = 0; isc <= MAX_ISC; isc++) {
6316c1dd652SFei Li             rc = kvm_s390_modify_ais_mode(fs, isc, SIC_IRQ_MODE_ALL);
6326c1dd652SFei Li             if (rc) {
6336c1dd652SFei Li                 error_report("Failed to reset ais mode for isc %d: %s",
6346c1dd652SFei Li                              isc, strerror(-rc));
6356c1dd652SFei Li             }
6366c1dd652SFei Li         }
6376c1dd652SFei Li     }
6386c1dd652SFei Li 
6397b35d0c4SCornelia Huck     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
6407b35d0c4SCornelia Huck     if (rc) {
6417b35d0c4SCornelia Huck         trace_flic_reset_failed(errno);
6427b35d0c4SCornelia Huck     }
6437b35d0c4SCornelia Huck 
6447b35d0c4SCornelia Huck     flic_enable_pfault(flic);
6457b35d0c4SCornelia Huck }
6467b35d0c4SCornelia Huck 
6477b35d0c4SCornelia Huck static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
6487b35d0c4SCornelia Huck {
6497b35d0c4SCornelia Huck     DeviceClass *dc = DEVICE_CLASS(oc);
65003cf077aSCornelia Huck     S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
6517b35d0c4SCornelia Huck 
6525cbab1bfSHalil Pasic     KVM_S390_FLIC_CLASS(oc)->parent_realize = dc->realize;
6537b35d0c4SCornelia Huck     dc->realize = kvm_s390_flic_realize;
654f2cab7f1SCornelia Huck     dc->vmsd = &kvm_s390_flic_vmstate;
6557b35d0c4SCornelia Huck     dc->reset = kvm_s390_flic_reset;
65603cf077aSCornelia Huck     fsc->register_io_adapter = kvm_s390_register_io_adapter;
657d426d9fbSCornelia Huck     fsc->io_adapter_map = kvm_s390_io_adapter_map;
658d426d9fbSCornelia Huck     fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
659d426d9fbSCornelia Huck     fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
6609eccb862SHalil Pasic     fsc->clear_io_irq = kvm_s390_clear_io_flic;
6616c1dd652SFei Li     fsc->modify_ais_mode = kvm_s390_modify_ais_mode;
6621622ffd5SYi Min Zhao     fsc->inject_airq = kvm_s390_inject_airq;
663e6505d53SDavid Hildenbrand     fsc->inject_service = kvm_s390_inject_service;
664e6505d53SDavid Hildenbrand     fsc->inject_io = kvm_s390_inject_io;
665e6505d53SDavid Hildenbrand     fsc->inject_crw_mchk = kvm_s390_inject_crw_mchk;
6667b35d0c4SCornelia Huck }
6677b35d0c4SCornelia Huck 
6687b35d0c4SCornelia Huck static const TypeInfo kvm_s390_flic_info = {
6697b35d0c4SCornelia Huck     .name          = TYPE_KVM_S390_FLIC,
6707b35d0c4SCornelia Huck     .parent        = TYPE_S390_FLIC_COMMON,
6717b35d0c4SCornelia Huck     .instance_size = sizeof(KVMS390FLICState),
6725cbab1bfSHalil Pasic     .class_size    = sizeof(KVMS390FLICStateClass),
6737b35d0c4SCornelia Huck     .class_init    = kvm_s390_flic_class_init,
6747b35d0c4SCornelia Huck };
6757b35d0c4SCornelia Huck 
6767b35d0c4SCornelia Huck static void kvm_s390_flic_register_types(void)
6777b35d0c4SCornelia Huck {
6787b35d0c4SCornelia Huck     type_register_static(&kvm_s390_flic_info);
6797b35d0c4SCornelia Huck }
6807b35d0c4SCornelia Huck 
6817b35d0c4SCornelia Huck type_init(kvm_s390_flic_register_types)
682