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