1 /* 2 * QEMU S390x KVM floating interrupt controller (flic) 3 * 4 * Copyright 2014 IBM Corp. 5 * Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com> 6 * 7 * This work is licensed under the terms of the GNU GPL, version 2 or (at 8 * your option) any later version. See the COPYING file in the top-level 9 * directory. 10 */ 11 12 #include <sys/ioctl.h> 13 #include "qemu/error-report.h" 14 #include "hw/sysbus.h" 15 #include "sysemu/kvm.h" 16 #include "migration/qemu-file.h" 17 #include "hw/s390x/s390_flic.h" 18 #include "trace.h" 19 20 #define FLIC_SAVE_INITIAL_SIZE getpagesize() 21 #define FLIC_FAILED (-1UL) 22 #define FLIC_SAVEVM_VERSION 1 23 24 void s390_flic_init(void) 25 { 26 DeviceState *dev; 27 int r; 28 29 if (kvm_enabled()) { 30 dev = qdev_create(NULL, "s390-flic"); 31 object_property_add_child(qdev_get_machine(), "s390-flic", 32 OBJECT(dev), NULL); 33 r = qdev_init(dev); 34 if (r) { 35 error_report("flic: couldn't create qdev"); 36 } 37 } 38 } 39 40 /** 41 * flic_get_all_irqs - store all pending irqs in buffer 42 * @buf: pointer to buffer which is passed to kernel 43 * @len: length of buffer 44 * @flic: pointer to flic device state 45 * 46 * Returns: -ENOMEM if buffer is too small, 47 * -EINVAL if attr.group is invalid, 48 * -EFAULT if copying to userspace failed, 49 * on success return number of stored interrupts 50 */ 51 static int flic_get_all_irqs(KVMS390FLICState *flic, 52 void *buf, int len) 53 { 54 struct kvm_device_attr attr = { 55 .group = KVM_DEV_FLIC_GET_ALL_IRQS, 56 .addr = (uint64_t) buf, 57 .attr = len, 58 }; 59 int rc; 60 61 rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr); 62 63 return rc == -1 ? -errno : rc; 64 } 65 66 /** flic_enqueue_irqs - returns 0 on success 67 * @buf: pointer to buffer which is passed to kernel 68 * @len: length of buffer 69 * @flic: pointer to flic device state 70 * 71 * Returns: -EINVAL if attr.group is unknown 72 */ 73 static int flic_enqueue_irqs(void *buf, uint64_t len, 74 KVMS390FLICState *flic) 75 { 76 int rc; 77 struct kvm_device_attr attr = { 78 .group = KVM_DEV_FLIC_ENQUEUE, 79 .addr = (uint64_t) buf, 80 .attr = len, 81 }; 82 83 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); 84 85 return rc ? -errno : 0; 86 } 87 88 /** 89 * __get_all_irqs - store all pending irqs in buffer 90 * @flic: pointer to flic device state 91 * @buf: pointer to pointer to a buffer 92 * @len: length of buffer 93 * 94 * Returns: return value of flic_get_all_irqs 95 * Note: Retry and increase buffer size until flic_get_all_irqs 96 * either returns a value >= 0 or a negative error code. 97 * -ENOMEM is an exception, which means the buffer is too small 98 * and we should try again. Other negative error codes can be 99 * -EFAULT and -EINVAL which we ignore at this point 100 */ 101 static int __get_all_irqs(KVMS390FLICState *flic, 102 void **buf, int len) 103 { 104 int r; 105 106 do { 107 /* returns -ENOMEM if buffer is too small and number 108 * of queued interrupts on success */ 109 r = flic_get_all_irqs(flic, *buf, len); 110 if (r >= 0) { 111 break; 112 } 113 len *= 2; 114 *buf = g_try_realloc(*buf, len); 115 if (!buf) { 116 return -ENOMEM; 117 } 118 } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER); 119 120 return r; 121 } 122 123 /** 124 * kvm_flic_save - Save pending floating interrupts 125 * @f: QEMUFile containing migration state 126 * @opaque: pointer to flic device state 127 * 128 * Note: Pass buf and len to kernel. Start with one page and 129 * increase until buffer is sufficient or maxium size is 130 * reached 131 */ 132 static void kvm_flic_save(QEMUFile *f, void *opaque) 133 { 134 KVMS390FLICState *flic = opaque; 135 int len = FLIC_SAVE_INITIAL_SIZE; 136 void *buf; 137 int count; 138 139 buf = g_try_malloc0(len); 140 if (!buf) { 141 /* Storing FLIC_FAILED into the count field here will cause the 142 * target system to fail when attempting to load irqs from the 143 * migration state */ 144 error_report("flic: couldn't allocate memory"); 145 qemu_put_be64(f, FLIC_FAILED); 146 return; 147 } 148 149 count = __get_all_irqs(flic, &buf, len); 150 if (count < 0) { 151 error_report("flic: couldn't retrieve irqs from kernel, rc %d", 152 count); 153 /* Storing FLIC_FAILED into the count field here will cause the 154 * target system to fail when attempting to load irqs from the 155 * migration state */ 156 qemu_put_be64(f, FLIC_FAILED); 157 } else { 158 qemu_put_be64(f, count); 159 qemu_put_buffer(f, (uint8_t *) buf, 160 count * sizeof(struct kvm_s390_irq)); 161 } 162 g_free(buf); 163 } 164 165 /** 166 * kvm_flic_load - Load pending floating interrupts 167 * @f: QEMUFile containing migration state 168 * @opaque: pointer to flic device state 169 * @version_id: version id for migration 170 * 171 * Returns: value of flic_enqueue_irqs, -EINVAL on error 172 * Note: Do nothing when no interrupts where stored 173 * in QEMUFile 174 */ 175 static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id) 176 { 177 uint64_t len = 0; 178 uint64_t count = 0; 179 void *buf = NULL; 180 int r = 0; 181 182 if (version_id != FLIC_SAVEVM_VERSION) { 183 r = -EINVAL; 184 goto out; 185 } 186 187 count = qemu_get_be64(f); 188 len = count * sizeof(struct kvm_s390_irq); 189 if (count == FLIC_FAILED) { 190 r = -EINVAL; 191 goto out; 192 } 193 if (count == 0) { 194 r = 0; 195 goto out; 196 } 197 buf = g_try_malloc0(len); 198 if (!buf) { 199 r = -ENOMEM; 200 goto out; 201 } 202 203 if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) { 204 r = -EINVAL; 205 goto out_free; 206 } 207 r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque); 208 209 out_free: 210 g_free(buf); 211 out: 212 return r; 213 } 214 215 static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) 216 { 217 KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); 218 struct kvm_create_device cd = {0}; 219 int ret; 220 221 flic_state->fd = -1; 222 if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) { 223 trace_flic_no_device_api(errno); 224 return; 225 } 226 227 cd.type = KVM_DEV_TYPE_FLIC; 228 ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); 229 if (ret < 0) { 230 trace_flic_create_device(errno); 231 return; 232 } 233 flic_state->fd = cd.fd; 234 235 /* Register savevm handler for floating interrupts */ 236 register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save, 237 kvm_flic_load, (void *) flic_state); 238 } 239 240 static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp) 241 { 242 KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); 243 244 unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state); 245 } 246 247 static void kvm_s390_flic_reset(DeviceState *dev) 248 { 249 KVMS390FLICState *flic = KVM_S390_FLIC(dev); 250 struct kvm_device_attr attr = { 251 .group = KVM_DEV_FLIC_CLEAR_IRQS, 252 }; 253 int rc = 0; 254 255 if (flic->fd == -1) { 256 return; 257 } 258 259 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); 260 if (rc) { 261 trace_flic_reset_failed(errno); 262 } 263 } 264 265 static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) 266 { 267 DeviceClass *dc = DEVICE_CLASS(oc); 268 269 dc->realize = kvm_s390_flic_realize; 270 dc->unrealize = kvm_s390_flic_unrealize; 271 dc->reset = kvm_s390_flic_reset; 272 } 273 274 static const TypeInfo kvm_s390_flic_info = { 275 .name = TYPE_KVM_S390_FLIC, 276 .parent = TYPE_SYS_BUS_DEVICE, 277 .instance_size = sizeof(KVMS390FLICState), 278 .class_init = kvm_s390_flic_class_init, 279 }; 280 281 static void kvm_s390_flic_register_types(void) 282 { 283 type_register_static(&kvm_s390_flic_info); 284 } 285 286 type_init(kvm_s390_flic_register_types) 287