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" 1467043607SCho, Yu-Chen #include "kvm/kvm_s390x.h" 157b35d0c4SCornelia Huck #include <sys/ioctl.h> 167b35d0c4SCornelia Huck #include "qemu/error-report.h" 170b8fa32fSMarkus Armbruster #include "qemu/module.h" 18f62f2109SHalil Pasic #include "qapi/error.h" 197b35d0c4SCornelia Huck #include "sysemu/kvm.h" 207b35d0c4SCornelia Huck #include "hw/s390x/s390_flic.h" 21d426d9fbSCornelia Huck #include "hw/s390x/adapter.h" 226c1dd652SFei Li #include "hw/s390x/css.h" 23ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h" 247b35d0c4SCornelia Huck #include "trace.h" 25db1015e9SEduardo Habkost #include "qom/object.h" 267b35d0c4SCornelia Huck 278e3b0cbbSMarc-André Lureau #define FLIC_SAVE_INITIAL_SIZE qemu_real_host_page_size() 287b35d0c4SCornelia Huck #define FLIC_FAILED (-1UL) 297b35d0c4SCornelia Huck #define FLIC_SAVEVM_VERSION 1 307b35d0c4SCornelia Huck 31b13f9bdfSEduardo Habkost struct KVMS390FLICState{ 327b35d0c4SCornelia Huck S390FLICState parent_obj; 337b35d0c4SCornelia Huck 347b35d0c4SCornelia Huck uint32_t fd; 359eccb862SHalil Pasic bool clear_io_supported; 36b13f9bdfSEduardo Habkost }; 377b35d0c4SCornelia Huck 38c21a6106SDavid Hildenbrand static KVMS390FLICState *s390_get_kvm_flic(S390FLICState *fs) 39c21a6106SDavid Hildenbrand { 40c21a6106SDavid Hildenbrand static KVMS390FLICState *flic; 41c21a6106SDavid Hildenbrand 42c21a6106SDavid Hildenbrand if (!flic) { 43c21a6106SDavid Hildenbrand /* we only have one flic device, so this is fine to cache */ 44c21a6106SDavid Hildenbrand flic = KVM_S390_FLIC(fs); 45c21a6106SDavid Hildenbrand } 46c21a6106SDavid Hildenbrand return flic; 47c21a6106SDavid Hildenbrand } 48c21a6106SDavid Hildenbrand 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 125e6505d53SDavid Hildenbrand static void kvm_s390_inject_flic(S390FLICState *fs, struct kvm_s390_irq *irq) 126bbd8bb8eSCornelia Huck { 127e6505d53SDavid Hildenbrand static bool use_flic = true; 128e6505d53SDavid Hildenbrand int r; 129bbd8bb8eSCornelia Huck 130e6505d53SDavid Hildenbrand if (use_flic) { 131c21a6106SDavid Hildenbrand r = flic_enqueue_irqs(irq, sizeof(*irq), s390_get_kvm_flic(fs)); 132e6505d53SDavid Hildenbrand if (r == -ENOSYS) { 133e6505d53SDavid Hildenbrand use_flic = false; 134bbd8bb8eSCornelia Huck } 135e6505d53SDavid Hildenbrand if (!r) { 136e6505d53SDavid Hildenbrand return; 137e6505d53SDavid Hildenbrand } 138e6505d53SDavid Hildenbrand } 139e6505d53SDavid Hildenbrand /* fallback to legacy KVM IOCTL in case FLIC fails */ 140e6505d53SDavid Hildenbrand kvm_s390_floating_interrupt_legacy(irq); 141e6505d53SDavid Hildenbrand } 142e6505d53SDavid Hildenbrand 143e6505d53SDavid Hildenbrand static void kvm_s390_inject_service(S390FLICState *fs, uint32_t parm) 144e6505d53SDavid Hildenbrand { 145e6505d53SDavid Hildenbrand struct kvm_s390_irq irq = { 146e6505d53SDavid Hildenbrand .type = KVM_S390_INT_SERVICE, 147e6505d53SDavid Hildenbrand .u.ext.ext_params = parm, 148e6505d53SDavid Hildenbrand }; 149e6505d53SDavid Hildenbrand 150e6505d53SDavid Hildenbrand kvm_s390_inject_flic(fs, &irq); 151e6505d53SDavid Hildenbrand } 152e6505d53SDavid Hildenbrand 153e6505d53SDavid Hildenbrand static void kvm_s390_inject_io(S390FLICState *fs, uint16_t subchannel_id, 154e6505d53SDavid Hildenbrand uint16_t subchannel_nr, uint32_t io_int_parm, 155e6505d53SDavid Hildenbrand uint32_t io_int_word) 156e6505d53SDavid Hildenbrand { 157e6505d53SDavid Hildenbrand struct kvm_s390_irq irq = { 158e6505d53SDavid Hildenbrand .u.io.subchannel_id = subchannel_id, 159e6505d53SDavid Hildenbrand .u.io.subchannel_nr = subchannel_nr, 160e6505d53SDavid Hildenbrand .u.io.io_int_parm = io_int_parm, 161e6505d53SDavid Hildenbrand .u.io.io_int_word = io_int_word, 162e6505d53SDavid Hildenbrand }; 163e6505d53SDavid Hildenbrand 164e6505d53SDavid Hildenbrand if (io_int_word & IO_INT_WORD_AI) { 165e6505d53SDavid Hildenbrand irq.type = KVM_S390_INT_IO(1, 0, 0, 0); 166e6505d53SDavid Hildenbrand } else { 167e6505d53SDavid Hildenbrand irq.type = KVM_S390_INT_IO(0, (subchannel_id & 0xff00) >> 8, 168e6505d53SDavid Hildenbrand (subchannel_id & 0x0006), 169e6505d53SDavid Hildenbrand subchannel_nr); 170e6505d53SDavid Hildenbrand } 171e6505d53SDavid Hildenbrand kvm_s390_inject_flic(fs, &irq); 172e6505d53SDavid Hildenbrand } 173e6505d53SDavid Hildenbrand 174e6505d53SDavid Hildenbrand static void kvm_s390_inject_crw_mchk(S390FLICState *fs) 175e6505d53SDavid Hildenbrand { 176e6505d53SDavid Hildenbrand struct kvm_s390_irq irq = { 177e6505d53SDavid Hildenbrand .type = KVM_S390_MCHK, 178e6505d53SDavid Hildenbrand .u.mchk.cr14 = CR14_CHANNEL_REPORT_SC, 179e6505d53SDavid Hildenbrand .u.mchk.mcic = s390_build_validity_mcic() | MCIC_SC_CP, 180e6505d53SDavid Hildenbrand }; 181e6505d53SDavid Hildenbrand 182e6505d53SDavid Hildenbrand kvm_s390_inject_flic(fs, &irq); 183bbd8bb8eSCornelia Huck } 184bbd8bb8eSCornelia Huck 1859eccb862SHalil Pasic static int kvm_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id, 1869eccb862SHalil Pasic uint16_t subchannel_nr) 1879eccb862SHalil Pasic { 188c21a6106SDavid Hildenbrand KVMS390FLICState *flic = s390_get_kvm_flic(fs); 1899eccb862SHalil Pasic int rc; 1909eccb862SHalil Pasic uint32_t sid = subchannel_id << 16 | subchannel_nr; 1919eccb862SHalil Pasic struct kvm_device_attr attr = { 1929eccb862SHalil Pasic .group = KVM_DEV_FLIC_CLEAR_IO_IRQ, 1939eccb862SHalil Pasic .addr = (uint64_t) &sid, 1949eccb862SHalil Pasic .attr = sizeof(sid), 1959eccb862SHalil Pasic }; 1969eccb862SHalil Pasic if (unlikely(!flic->clear_io_supported)) { 1979eccb862SHalil Pasic return -ENOSYS; 1989eccb862SHalil Pasic } 1999eccb862SHalil Pasic rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); 2009eccb862SHalil Pasic return rc ? -errno : 0; 2019eccb862SHalil Pasic } 2029eccb862SHalil Pasic 2036c1dd652SFei Li static int kvm_s390_modify_ais_mode(S390FLICState *fs, uint8_t isc, 2046c1dd652SFei Li uint16_t mode) 2056c1dd652SFei Li { 206c21a6106SDavid Hildenbrand KVMS390FLICState *flic = s390_get_kvm_flic(fs); 2076c1dd652SFei Li struct kvm_s390_ais_req req = { 2086c1dd652SFei Li .isc = isc, 2096c1dd652SFei Li .mode = mode, 2106c1dd652SFei Li }; 2116c1dd652SFei Li struct kvm_device_attr attr = { 2126c1dd652SFei Li .group = KVM_DEV_FLIC_AISM, 2136c1dd652SFei Li .addr = (uint64_t)&req, 2146c1dd652SFei Li }; 2156c1dd652SFei Li 2166c1dd652SFei Li if (!fs->ais_supported) { 2176c1dd652SFei Li return -ENOSYS; 2186c1dd652SFei Li } 2196c1dd652SFei Li 2206c1dd652SFei Li return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0; 2216c1dd652SFei Li } 2226c1dd652SFei Li 2231622ffd5SYi Min Zhao static int kvm_s390_inject_airq(S390FLICState *fs, uint8_t type, 2241622ffd5SYi Min Zhao uint8_t isc, uint8_t flags) 2251622ffd5SYi Min Zhao { 226c21a6106SDavid Hildenbrand KVMS390FLICState *flic = s390_get_kvm_flic(fs); 2271622ffd5SYi Min Zhao uint32_t id = css_get_adapter_id(type, isc); 2281622ffd5SYi Min Zhao struct kvm_device_attr attr = { 2291622ffd5SYi Min Zhao .group = KVM_DEV_FLIC_AIRQ_INJECT, 2301622ffd5SYi Min Zhao .attr = id, 2311622ffd5SYi Min Zhao }; 2321622ffd5SYi Min Zhao 2331622ffd5SYi Min Zhao if (!fs->ais_supported) { 2341622ffd5SYi Min Zhao return -ENOSYS; 2351622ffd5SYi Min Zhao } 2361622ffd5SYi Min Zhao 2371622ffd5SYi Min Zhao return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0; 2381622ffd5SYi Min Zhao } 2391622ffd5SYi Min Zhao 2407b35d0c4SCornelia Huck /** 2417b35d0c4SCornelia Huck * __get_all_irqs - store all pending irqs in buffer 2427b35d0c4SCornelia Huck * @flic: pointer to flic device state 2437b35d0c4SCornelia Huck * @buf: pointer to pointer to a buffer 2447b35d0c4SCornelia Huck * @len: length of buffer 2457b35d0c4SCornelia Huck * 2467b35d0c4SCornelia Huck * Returns: return value of flic_get_all_irqs 2477b35d0c4SCornelia Huck * Note: Retry and increase buffer size until flic_get_all_irqs 2487b35d0c4SCornelia Huck * either returns a value >= 0 or a negative error code. 2497b35d0c4SCornelia Huck * -ENOMEM is an exception, which means the buffer is too small 2507b35d0c4SCornelia Huck * and we should try again. Other negative error codes can be 2517b35d0c4SCornelia Huck * -EFAULT and -EINVAL which we ignore at this point 2527b35d0c4SCornelia Huck */ 2537b35d0c4SCornelia Huck static int __get_all_irqs(KVMS390FLICState *flic, 2547b35d0c4SCornelia Huck void **buf, int len) 2557b35d0c4SCornelia Huck { 2567b35d0c4SCornelia Huck int r; 2577b35d0c4SCornelia Huck 2587b35d0c4SCornelia Huck do { 2597b35d0c4SCornelia Huck /* returns -ENOMEM if buffer is too small and number 2607b35d0c4SCornelia Huck * of queued interrupts on success */ 2617b35d0c4SCornelia Huck r = flic_get_all_irqs(flic, *buf, len); 2627b35d0c4SCornelia Huck if (r >= 0) { 2637b35d0c4SCornelia Huck break; 2647b35d0c4SCornelia Huck } 2657b35d0c4SCornelia Huck len *= 2; 2667b35d0c4SCornelia Huck *buf = g_try_realloc(*buf, len); 2677b35d0c4SCornelia Huck if (!buf) { 2687b35d0c4SCornelia Huck return -ENOMEM; 2697b35d0c4SCornelia Huck } 2707b35d0c4SCornelia Huck } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER); 2717b35d0c4SCornelia Huck 2727b35d0c4SCornelia Huck return r; 2737b35d0c4SCornelia Huck } 2747b35d0c4SCornelia Huck 27503cf077aSCornelia Huck static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id, 27603cf077aSCornelia Huck uint8_t isc, bool swap, 2771497c160SFei Li bool is_maskable, uint8_t flags) 27803cf077aSCornelia Huck { 27903cf077aSCornelia Huck struct kvm_s390_io_adapter adapter = { 28003cf077aSCornelia Huck .id = id, 28103cf077aSCornelia Huck .isc = isc, 28203cf077aSCornelia Huck .maskable = is_maskable, 28303cf077aSCornelia Huck .swap = swap, 2841497c160SFei Li .flags = flags, 28503cf077aSCornelia Huck }; 28603cf077aSCornelia Huck KVMS390FLICState *flic = KVM_S390_FLIC(fs); 2879be38598SEduardo Habkost int r; 28803cf077aSCornelia Huck struct kvm_device_attr attr = { 28903cf077aSCornelia Huck .group = KVM_DEV_FLIC_ADAPTER_REGISTER, 29003cf077aSCornelia Huck .addr = (uint64_t)&adapter, 29103cf077aSCornelia Huck }; 29203cf077aSCornelia Huck 2934cbd6c41SFei Li if (!kvm_gsi_routing_enabled()) { 29408da527fSCornelia Huck /* nothing to do */ 29508da527fSCornelia Huck return 0; 29603cf077aSCornelia Huck } 29703cf077aSCornelia Huck 29803cf077aSCornelia Huck r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); 29903cf077aSCornelia Huck 3009be38598SEduardo Habkost return r ? -errno : 0; 30103cf077aSCornelia Huck } 30203cf077aSCornelia Huck 303d426d9fbSCornelia Huck static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id, 304d426d9fbSCornelia Huck uint64_t map_addr, bool do_map) 305d426d9fbSCornelia Huck { 306d426d9fbSCornelia Huck struct kvm_s390_io_adapter_req req = { 307d426d9fbSCornelia Huck .id = id, 308d426d9fbSCornelia Huck .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP, 309d426d9fbSCornelia Huck .addr = map_addr, 310d426d9fbSCornelia Huck }; 311d426d9fbSCornelia Huck struct kvm_device_attr attr = { 312d426d9fbSCornelia Huck .group = KVM_DEV_FLIC_ADAPTER_MODIFY, 313d426d9fbSCornelia Huck .addr = (uint64_t)&req, 314d426d9fbSCornelia Huck }; 315c21a6106SDavid Hildenbrand KVMS390FLICState *flic = s390_get_kvm_flic(fs); 316d426d9fbSCornelia Huck int r; 317d426d9fbSCornelia Huck 3184cbd6c41SFei Li if (!kvm_gsi_routing_enabled()) { 31908da527fSCornelia Huck /* nothing to do */ 32008da527fSCornelia Huck return 0; 321d426d9fbSCornelia Huck } 322d426d9fbSCornelia Huck 323d426d9fbSCornelia Huck r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); 324d426d9fbSCornelia Huck return r ? -errno : 0; 325d426d9fbSCornelia Huck } 326d426d9fbSCornelia Huck 32748663349SPaolo Bonzini static int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) 32848663349SPaolo Bonzini { 32948663349SPaolo Bonzini struct kvm_irq_routing_entry kroute = {}; 33048663349SPaolo Bonzini int virq; 33148663349SPaolo Bonzini 33248663349SPaolo Bonzini if (!kvm_gsi_routing_enabled()) { 33348663349SPaolo Bonzini return -ENOSYS; 33448663349SPaolo Bonzini } 33548663349SPaolo Bonzini 33648663349SPaolo Bonzini virq = kvm_irqchip_get_virq(s); 33748663349SPaolo Bonzini if (virq < 0) { 33848663349SPaolo Bonzini return virq; 33948663349SPaolo Bonzini } 34048663349SPaolo Bonzini 34148663349SPaolo Bonzini kroute.gsi = virq; 34248663349SPaolo Bonzini kroute.type = KVM_IRQ_ROUTING_S390_ADAPTER; 34348663349SPaolo Bonzini kroute.flags = 0; 34448663349SPaolo Bonzini kroute.u.adapter.summary_addr = adapter->summary_addr; 34548663349SPaolo Bonzini kroute.u.adapter.ind_addr = adapter->ind_addr; 34648663349SPaolo Bonzini kroute.u.adapter.summary_offset = adapter->summary_offset; 34748663349SPaolo Bonzini kroute.u.adapter.ind_offset = adapter->ind_offset; 34848663349SPaolo Bonzini kroute.u.adapter.adapter_id = adapter->adapter_id; 34948663349SPaolo Bonzini 35048663349SPaolo Bonzini kvm_add_routing_entry(s, &kroute); 35148663349SPaolo Bonzini 35248663349SPaolo Bonzini return virq; 35348663349SPaolo Bonzini } 35448663349SPaolo Bonzini 355d426d9fbSCornelia Huck static int kvm_s390_add_adapter_routes(S390FLICState *fs, 356d426d9fbSCornelia Huck AdapterRoutes *routes) 357d426d9fbSCornelia Huck { 358d426d9fbSCornelia Huck int ret, i; 359d426d9fbSCornelia Huck uint64_t ind_offset = routes->adapter.ind_offset; 360d426d9fbSCornelia Huck 3613c5fd807SCornelia Huck if (!kvm_gsi_routing_enabled()) { 3623c5fd807SCornelia Huck return -ENOSYS; 3633c5fd807SCornelia Huck } 3643c5fd807SCornelia Huck 365d426d9fbSCornelia Huck for (i = 0; i < routes->num_routes; i++) { 366d426d9fbSCornelia Huck ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter); 367d426d9fbSCornelia Huck if (ret < 0) { 368d426d9fbSCornelia Huck goto out_undo; 369d426d9fbSCornelia Huck } 370d426d9fbSCornelia Huck routes->gsi[i] = ret; 371d426d9fbSCornelia Huck routes->adapter.ind_offset++; 372d426d9fbSCornelia Huck } 373c0194a00SJens Freimann kvm_irqchip_commit_routes(kvm_state); 374c0194a00SJens Freimann 375d426d9fbSCornelia Huck /* Restore passed-in structure to original state. */ 376d426d9fbSCornelia Huck routes->adapter.ind_offset = ind_offset; 377d426d9fbSCornelia Huck return 0; 378d426d9fbSCornelia Huck out_undo: 379d426d9fbSCornelia Huck while (--i >= 0) { 380d426d9fbSCornelia Huck kvm_irqchip_release_virq(kvm_state, routes->gsi[i]); 381d426d9fbSCornelia Huck routes->gsi[i] = -1; 382d426d9fbSCornelia Huck } 383d426d9fbSCornelia Huck routes->adapter.ind_offset = ind_offset; 384d426d9fbSCornelia Huck return ret; 385d426d9fbSCornelia Huck } 386d426d9fbSCornelia Huck 387d426d9fbSCornelia Huck static void kvm_s390_release_adapter_routes(S390FLICState *fs, 388d426d9fbSCornelia Huck AdapterRoutes *routes) 389d426d9fbSCornelia Huck { 390d426d9fbSCornelia Huck int i; 391d426d9fbSCornelia Huck 3923c5fd807SCornelia Huck if (!kvm_gsi_routing_enabled()) { 3933c5fd807SCornelia Huck return; 3943c5fd807SCornelia Huck } 3953c5fd807SCornelia Huck 396d426d9fbSCornelia Huck for (i = 0; i < routes->num_routes; i++) { 397d426d9fbSCornelia Huck if (routes->gsi[i] >= 0) { 398d426d9fbSCornelia Huck kvm_irqchip_release_virq(kvm_state, routes->gsi[i]); 399d426d9fbSCornelia Huck routes->gsi[i] = -1; 400d426d9fbSCornelia Huck } 401d426d9fbSCornelia Huck } 402d426d9fbSCornelia Huck } 403d426d9fbSCornelia Huck 4047b35d0c4SCornelia Huck /** 4057b35d0c4SCornelia Huck * kvm_flic_save - Save pending floating interrupts 4067b35d0c4SCornelia Huck * @f: QEMUFile containing migration state 4077b35d0c4SCornelia Huck * @opaque: pointer to flic device state 408f2cab7f1SCornelia Huck * @size: ignored 4097b35d0c4SCornelia Huck * 4107b35d0c4SCornelia Huck * Note: Pass buf and len to kernel. Start with one page and 411cced0d65SMichael Tokarev * increase until buffer is sufficient or maximum size is 4127b35d0c4SCornelia Huck * reached 4137b35d0c4SCornelia Huck */ 4142c21ee76SJianjun Duan static int kvm_flic_save(QEMUFile *f, void *opaque, size_t size, 4153ddba9a9SMarkus Armbruster const VMStateField *field, JSONWriter *vmdesc) 4167b35d0c4SCornelia Huck { 4177b35d0c4SCornelia Huck KVMS390FLICState *flic = opaque; 4187b35d0c4SCornelia Huck int len = FLIC_SAVE_INITIAL_SIZE; 4197b35d0c4SCornelia Huck void *buf; 4207b35d0c4SCornelia Huck int count; 421ba690c71SCornelia Huck int r = 0; 4227b35d0c4SCornelia Huck 4237b35d0c4SCornelia Huck flic_disable_wait_pfault((struct KVMS390FLICState *) opaque); 4247b35d0c4SCornelia Huck 4257b35d0c4SCornelia Huck buf = g_try_malloc0(len); 4267b35d0c4SCornelia Huck if (!buf) { 4277b35d0c4SCornelia Huck /* Storing FLIC_FAILED into the count field here will cause the 4287b35d0c4SCornelia Huck * target system to fail when attempting to load irqs from the 4297b35d0c4SCornelia Huck * migration state */ 4307b35d0c4SCornelia Huck error_report("flic: couldn't allocate memory"); 4317b35d0c4SCornelia Huck qemu_put_be64(f, FLIC_FAILED); 432ba690c71SCornelia Huck return -ENOMEM; 4337b35d0c4SCornelia Huck } 4347b35d0c4SCornelia Huck 4357b35d0c4SCornelia Huck count = __get_all_irqs(flic, &buf, len); 4367b35d0c4SCornelia Huck if (count < 0) { 4377b35d0c4SCornelia Huck error_report("flic: couldn't retrieve irqs from kernel, rc %d", 4387b35d0c4SCornelia Huck count); 4397b35d0c4SCornelia Huck /* Storing FLIC_FAILED into the count field here will cause the 4407b35d0c4SCornelia Huck * target system to fail when attempting to load irqs from the 4417b35d0c4SCornelia Huck * migration state */ 4427b35d0c4SCornelia Huck qemu_put_be64(f, FLIC_FAILED); 443ba690c71SCornelia Huck r = count; 4447b35d0c4SCornelia Huck } else { 4457b35d0c4SCornelia Huck qemu_put_be64(f, count); 4467b35d0c4SCornelia Huck qemu_put_buffer(f, (uint8_t *) buf, 4477b35d0c4SCornelia Huck count * sizeof(struct kvm_s390_irq)); 4487b35d0c4SCornelia Huck } 4497b35d0c4SCornelia Huck g_free(buf); 4502c21ee76SJianjun Duan 451ba690c71SCornelia Huck return r; 4527b35d0c4SCornelia Huck } 4537b35d0c4SCornelia Huck 4547b35d0c4SCornelia Huck /** 4557b35d0c4SCornelia Huck * kvm_flic_load - Load pending floating interrupts 4567b35d0c4SCornelia Huck * @f: QEMUFile containing migration state 4577b35d0c4SCornelia Huck * @opaque: pointer to flic device state 458f2cab7f1SCornelia Huck * @size: ignored 4597b35d0c4SCornelia Huck * 4607b35d0c4SCornelia Huck * Returns: value of flic_enqueue_irqs, -EINVAL on error 4617b35d0c4SCornelia Huck * Note: Do nothing when no interrupts where stored 4627b35d0c4SCornelia Huck * in QEMUFile 4637b35d0c4SCornelia Huck */ 4642c21ee76SJianjun Duan static int kvm_flic_load(QEMUFile *f, void *opaque, size_t size, 46503fee66fSMarc-André Lureau const VMStateField *field) 4667b35d0c4SCornelia Huck { 4677b35d0c4SCornelia Huck uint64_t len = 0; 4687b35d0c4SCornelia Huck uint64_t count = 0; 4697b35d0c4SCornelia Huck void *buf = NULL; 4707b35d0c4SCornelia Huck int r = 0; 4717b35d0c4SCornelia Huck 4727b35d0c4SCornelia Huck flic_enable_pfault((struct KVMS390FLICState *) opaque); 4737b35d0c4SCornelia Huck 4747b35d0c4SCornelia Huck count = qemu_get_be64(f); 4757b35d0c4SCornelia Huck len = count * sizeof(struct kvm_s390_irq); 4767b35d0c4SCornelia Huck if (count == FLIC_FAILED) { 47765569bbfSDaniel Henrique Barboza return -EINVAL; 4787b35d0c4SCornelia Huck } 4797b35d0c4SCornelia Huck if (count == 0) { 48065569bbfSDaniel Henrique Barboza return 0; 4817b35d0c4SCornelia Huck } 4827b35d0c4SCornelia Huck buf = g_try_malloc0(len); 4837b35d0c4SCornelia Huck if (!buf) { 48465569bbfSDaniel Henrique Barboza return -ENOMEM; 4857b35d0c4SCornelia Huck } 4867b35d0c4SCornelia Huck 4877b35d0c4SCornelia Huck if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) { 4887b35d0c4SCornelia Huck r = -EINVAL; 4897b35d0c4SCornelia Huck goto out_free; 4907b35d0c4SCornelia Huck } 4917b35d0c4SCornelia Huck r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque); 4927b35d0c4SCornelia Huck 4937b35d0c4SCornelia Huck out_free: 4947b35d0c4SCornelia Huck g_free(buf); 4957b35d0c4SCornelia Huck return r; 4967b35d0c4SCornelia Huck } 4977b35d0c4SCornelia Huck 498e7be8d49SYi Min Zhao typedef struct KVMS390FLICStateMigTmp { 499e7be8d49SYi Min Zhao KVMS390FLICState *parent; 500e7be8d49SYi Min Zhao uint8_t simm; 501e7be8d49SYi Min Zhao uint8_t nimm; 502e7be8d49SYi Min Zhao } KVMS390FLICStateMigTmp; 503e7be8d49SYi Min Zhao 50444b1ff31SDr. David Alan Gilbert static int kvm_flic_ais_pre_save(void *opaque) 505e7be8d49SYi Min Zhao { 506e7be8d49SYi Min Zhao KVMS390FLICStateMigTmp *tmp = opaque; 507e7be8d49SYi Min Zhao KVMS390FLICState *flic = tmp->parent; 508e7be8d49SYi Min Zhao struct kvm_s390_ais_all ais; 509e7be8d49SYi Min Zhao struct kvm_device_attr attr = { 510e7be8d49SYi Min Zhao .group = KVM_DEV_FLIC_AISM_ALL, 511e7be8d49SYi Min Zhao .addr = (uint64_t)&ais, 512e7be8d49SYi Min Zhao .attr = sizeof(ais), 513e7be8d49SYi Min Zhao }; 514e7be8d49SYi Min Zhao 515e7be8d49SYi Min Zhao if (ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr)) { 516e7be8d49SYi Min Zhao error_report("Failed to retrieve kvm flic ais states"); 51744b1ff31SDr. David Alan Gilbert return -EINVAL; 518e7be8d49SYi Min Zhao } 519e7be8d49SYi Min Zhao 520e7be8d49SYi Min Zhao tmp->simm = ais.simm; 521e7be8d49SYi Min Zhao tmp->nimm = ais.nimm; 52244b1ff31SDr. David Alan Gilbert 52344b1ff31SDr. David Alan Gilbert return 0; 524e7be8d49SYi Min Zhao } 525e7be8d49SYi Min Zhao 526e7be8d49SYi Min Zhao static int kvm_flic_ais_post_load(void *opaque, int version_id) 527e7be8d49SYi Min Zhao { 528e7be8d49SYi Min Zhao KVMS390FLICStateMigTmp *tmp = opaque; 529e7be8d49SYi Min Zhao KVMS390FLICState *flic = tmp->parent; 530e7be8d49SYi Min Zhao struct kvm_s390_ais_all ais = { 531e7be8d49SYi Min Zhao .simm = tmp->simm, 532e7be8d49SYi Min Zhao .nimm = tmp->nimm, 533e7be8d49SYi Min Zhao }; 534e7be8d49SYi Min Zhao struct kvm_device_attr attr = { 535e7be8d49SYi Min Zhao .group = KVM_DEV_FLIC_AISM_ALL, 536e7be8d49SYi Min Zhao .addr = (uint64_t)&ais, 537e7be8d49SYi Min Zhao }; 538e7be8d49SYi Min Zhao 539e7be8d49SYi Min Zhao /* This can happen when the user mis-configures its guests in an 540e7be8d49SYi Min Zhao * incompatible fashion or without a CPU model. For example using 541e7be8d49SYi Min Zhao * qemu with -cpu host (which is not migration safe) and do a 542e7be8d49SYi Min Zhao * migration from a host that has AIS to a host that has no AIS. 543e7be8d49SYi Min Zhao * In that case the target system will reject the migration here. 544e7be8d49SYi Min Zhao */ 545e7be8d49SYi Min Zhao if (!ais_needed(flic)) { 546e7be8d49SYi Min Zhao return -ENOSYS; 547e7be8d49SYi Min Zhao } 548e7be8d49SYi Min Zhao 549e7be8d49SYi Min Zhao return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0; 550e7be8d49SYi Min Zhao } 551e7be8d49SYi Min Zhao 552e7be8d49SYi Min Zhao static const VMStateDescription kvm_s390_flic_ais_tmp = { 553e7be8d49SYi Min Zhao .name = "s390-flic-ais-tmp", 554e7be8d49SYi Min Zhao .pre_save = kvm_flic_ais_pre_save, 555e7be8d49SYi Min Zhao .post_load = kvm_flic_ais_post_load, 55645b1f81dSRichard Henderson .fields = (const VMStateField[]) { 557e7be8d49SYi Min Zhao VMSTATE_UINT8(simm, KVMS390FLICStateMigTmp), 558e7be8d49SYi Min Zhao VMSTATE_UINT8(nimm, KVMS390FLICStateMigTmp), 559e7be8d49SYi Min Zhao VMSTATE_END_OF_LIST() 560e7be8d49SYi Min Zhao } 561e7be8d49SYi Min Zhao }; 562e7be8d49SYi Min Zhao 563e7be8d49SYi Min Zhao static const VMStateDescription kvm_s390_flic_vmstate_ais = { 564e7be8d49SYi Min Zhao .name = "s390-flic/ais", 565e7be8d49SYi Min Zhao .version_id = 1, 566e7be8d49SYi Min Zhao .minimum_version_id = 1, 567e7be8d49SYi Min Zhao .needed = ais_needed, 56845b1f81dSRichard Henderson .fields = (const VMStateField[]) { 569e7be8d49SYi Min Zhao VMSTATE_WITH_TMP(KVMS390FLICState, KVMS390FLICStateMigTmp, 570e7be8d49SYi Min Zhao kvm_s390_flic_ais_tmp), 571e7be8d49SYi Min Zhao VMSTATE_END_OF_LIST() 572e7be8d49SYi Min Zhao } 573e7be8d49SYi Min Zhao }; 574e7be8d49SYi Min Zhao 575f2cab7f1SCornelia Huck static const VMStateDescription kvm_s390_flic_vmstate = { 576e7be8d49SYi Min Zhao /* should have been like kvm-s390-flic, 577e7be8d49SYi Min Zhao * can't change without breaking compat */ 578f2cab7f1SCornelia Huck .name = "s390-flic", 579f2cab7f1SCornelia Huck .version_id = FLIC_SAVEVM_VERSION, 580f2cab7f1SCornelia Huck .minimum_version_id = FLIC_SAVEVM_VERSION, 58145b1f81dSRichard Henderson .fields = (const VMStateField[]) { 582f2cab7f1SCornelia Huck { 583f2cab7f1SCornelia Huck .name = "irqs", 584f2cab7f1SCornelia Huck .info = &(const VMStateInfo) { 585f2cab7f1SCornelia Huck .name = "irqs", 586f2cab7f1SCornelia Huck .get = kvm_flic_load, 587f2cab7f1SCornelia Huck .put = kvm_flic_save, 588f2cab7f1SCornelia Huck }, 589f2cab7f1SCornelia Huck .flags = VMS_SINGLE, 590f2cab7f1SCornelia Huck }, 591f2cab7f1SCornelia Huck VMSTATE_END_OF_LIST() 592e7be8d49SYi Min Zhao }, 59345b1f81dSRichard Henderson .subsections = (const VMStateDescription * const []) { 594e7be8d49SYi Min Zhao &kvm_s390_flic_vmstate_ais, 595e7be8d49SYi Min Zhao NULL 596f2cab7f1SCornelia Huck } 597f2cab7f1SCornelia Huck }; 598f2cab7f1SCornelia Huck 599db1015e9SEduardo Habkost struct KVMS390FLICStateClass { 6005cbab1bfSHalil Pasic S390FLICStateClass parent_class; 6015cbab1bfSHalil Pasic DeviceRealize parent_realize; 602db1015e9SEduardo Habkost }; 603db1015e9SEduardo Habkost typedef struct KVMS390FLICStateClass KVMS390FLICStateClass; 6045cbab1bfSHalil Pasic 6058110fa1dSEduardo Habkost DECLARE_CLASS_CHECKERS(KVMS390FLICStateClass, KVM_S390_FLIC, 6068110fa1dSEduardo Habkost TYPE_KVM_S390_FLIC) 6075cbab1bfSHalil Pasic 6085cbab1bfSHalil Pasic 6097b35d0c4SCornelia Huck static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) 6107b35d0c4SCornelia Huck { 6117b35d0c4SCornelia Huck KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); 6127b35d0c4SCornelia Huck struct kvm_create_device cd = {0}; 6139eccb862SHalil Pasic struct kvm_device_attr test_attr = {0}; 6147b35d0c4SCornelia Huck int ret; 6158ca63ba8SMarkus Armbruster Error *err = NULL; 6167b35d0c4SCornelia Huck 6178ca63ba8SMarkus Armbruster KVM_S390_FLIC_GET_CLASS(dev)->parent_realize(dev, &err); 6188ca63ba8SMarkus Armbruster if (err) { 619d402c983SMarkus Armbruster error_propagate(errp, err); 620d402c983SMarkus Armbruster return; 6215cbab1bfSHalil Pasic } 6227b35d0c4SCornelia Huck flic_state->fd = -1; 6237b35d0c4SCornelia Huck 6247b35d0c4SCornelia Huck cd.type = KVM_DEV_TYPE_FLIC; 6257b35d0c4SCornelia Huck ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); 6267b35d0c4SCornelia Huck if (ret < 0) { 627d402c983SMarkus Armbruster error_setg_errno(errp, errno, "Creating the KVM device failed"); 6287b35d0c4SCornelia Huck trace_flic_create_device(errno); 629d402c983SMarkus Armbruster return; 6307b35d0c4SCornelia Huck } 6317b35d0c4SCornelia Huck flic_state->fd = cd.fd; 6327b35d0c4SCornelia Huck 6339eccb862SHalil Pasic /* Check clear_io_irq support */ 6349eccb862SHalil Pasic test_attr.group = KVM_DEV_FLIC_CLEAR_IO_IRQ; 6359eccb862SHalil Pasic flic_state->clear_io_supported = !ioctl(flic_state->fd, 6369eccb862SHalil Pasic KVM_HAS_DEVICE_ATTR, test_attr); 6377b35d0c4SCornelia Huck } 6387b35d0c4SCornelia Huck 6397b35d0c4SCornelia Huck static void kvm_s390_flic_reset(DeviceState *dev) 6407b35d0c4SCornelia Huck { 6417b35d0c4SCornelia Huck KVMS390FLICState *flic = KVM_S390_FLIC(dev); 6426c1dd652SFei Li S390FLICState *fs = S390_FLIC_COMMON(dev); 6437b35d0c4SCornelia Huck struct kvm_device_attr attr = { 6447b35d0c4SCornelia Huck .group = KVM_DEV_FLIC_CLEAR_IRQS, 6457b35d0c4SCornelia Huck }; 6467b35d0c4SCornelia Huck int rc = 0; 6476c1dd652SFei Li uint8_t isc; 6487b35d0c4SCornelia Huck 6497b35d0c4SCornelia Huck if (flic->fd == -1) { 6507b35d0c4SCornelia Huck return; 6517b35d0c4SCornelia Huck } 6527b35d0c4SCornelia Huck 6537b35d0c4SCornelia Huck flic_disable_wait_pfault(flic); 6547b35d0c4SCornelia Huck 6556c1dd652SFei Li if (fs->ais_supported) { 6566c1dd652SFei Li for (isc = 0; isc <= MAX_ISC; isc++) { 6576c1dd652SFei Li rc = kvm_s390_modify_ais_mode(fs, isc, SIC_IRQ_MODE_ALL); 6586c1dd652SFei Li if (rc) { 6596c1dd652SFei Li error_report("Failed to reset ais mode for isc %d: %s", 6606c1dd652SFei Li isc, strerror(-rc)); 6616c1dd652SFei Li } 6626c1dd652SFei Li } 6636c1dd652SFei Li } 6646c1dd652SFei Li 6657b35d0c4SCornelia Huck rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); 6667b35d0c4SCornelia Huck if (rc) { 6677b35d0c4SCornelia Huck trace_flic_reset_failed(errno); 6687b35d0c4SCornelia Huck } 6697b35d0c4SCornelia Huck 6707b35d0c4SCornelia Huck flic_enable_pfault(flic); 6717b35d0c4SCornelia Huck } 6727b35d0c4SCornelia Huck 6737b35d0c4SCornelia Huck static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) 6747b35d0c4SCornelia Huck { 6757b35d0c4SCornelia Huck DeviceClass *dc = DEVICE_CLASS(oc); 67603cf077aSCornelia Huck S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); 6776269aad7SZhao Liu KVMS390FLICStateClass *kfsc = KVM_S390_FLIC_CLASS(oc); 6787b35d0c4SCornelia Huck 6796269aad7SZhao Liu device_class_set_parent_realize(dc, kvm_s390_flic_realize, 6806269aad7SZhao Liu &kfsc->parent_realize); 681f2cab7f1SCornelia Huck dc->vmsd = &kvm_s390_flic_vmstate; 682*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, kvm_s390_flic_reset); 68303cf077aSCornelia Huck fsc->register_io_adapter = kvm_s390_register_io_adapter; 684d426d9fbSCornelia Huck fsc->io_adapter_map = kvm_s390_io_adapter_map; 685d426d9fbSCornelia Huck fsc->add_adapter_routes = kvm_s390_add_adapter_routes; 686d426d9fbSCornelia Huck fsc->release_adapter_routes = kvm_s390_release_adapter_routes; 6879eccb862SHalil Pasic fsc->clear_io_irq = kvm_s390_clear_io_flic; 6886c1dd652SFei Li fsc->modify_ais_mode = kvm_s390_modify_ais_mode; 6891622ffd5SYi Min Zhao fsc->inject_airq = kvm_s390_inject_airq; 690e6505d53SDavid Hildenbrand fsc->inject_service = kvm_s390_inject_service; 691e6505d53SDavid Hildenbrand fsc->inject_io = kvm_s390_inject_io; 692e6505d53SDavid Hildenbrand fsc->inject_crw_mchk = kvm_s390_inject_crw_mchk; 6937b35d0c4SCornelia Huck } 6947b35d0c4SCornelia Huck 6957b35d0c4SCornelia Huck static const TypeInfo kvm_s390_flic_info = { 6967b35d0c4SCornelia Huck .name = TYPE_KVM_S390_FLIC, 6977b35d0c4SCornelia Huck .parent = TYPE_S390_FLIC_COMMON, 6987b35d0c4SCornelia Huck .instance_size = sizeof(KVMS390FLICState), 6995cbab1bfSHalil Pasic .class_size = sizeof(KVMS390FLICStateClass), 7007b35d0c4SCornelia Huck .class_init = kvm_s390_flic_class_init, 7017b35d0c4SCornelia Huck }; 7027b35d0c4SCornelia Huck 7037b35d0c4SCornelia Huck static void kvm_s390_flic_register_types(void) 7047b35d0c4SCornelia Huck { 7057b35d0c4SCornelia Huck type_register_static(&kvm_s390_flic_info); 7067b35d0c4SCornelia Huck } 7077b35d0c4SCornelia Huck 7087b35d0c4SCornelia Huck type_init(kvm_s390_flic_register_types) 709