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