xref: /openbmc/qemu/hw/intc/s390_flic_kvm.c (revision 3257fc83)
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  *            Cornelia Huck <cornelia.huck@de.ibm.com>
7  *
8  * This work is licensed under the terms of the GNU GPL, version 2 or (at
9  * your option) any later version. See the COPYING file in the top-level
10  * directory.
11  */
12 
13 #include <sys/ioctl.h>
14 #include "qemu/error-report.h"
15 #include "hw/sysbus.h"
16 #include "sysemu/kvm.h"
17 #include "migration/qemu-file.h"
18 #include "hw/s390x/s390_flic.h"
19 #include "hw/s390x/adapter.h"
20 #include "trace.h"
21 
22 #define FLIC_SAVE_INITIAL_SIZE getpagesize()
23 #define FLIC_FAILED (-1UL)
24 #define FLIC_SAVEVM_VERSION 1
25 
26 typedef struct KVMS390FLICState {
27     S390FLICState parent_obj;
28 
29     uint32_t fd;
30 } KVMS390FLICState;
31 
32 DeviceState *s390_flic_kvm_create(void)
33 {
34     DeviceState *dev = NULL;
35 
36     if (kvm_enabled()) {
37         dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
38         object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
39                                   OBJECT(dev), NULL);
40     }
41     return dev;
42 }
43 
44 /**
45  * flic_get_all_irqs - store all pending irqs in buffer
46  * @buf: pointer to buffer which is passed to kernel
47  * @len: length of buffer
48  * @flic: pointer to flic device state
49  *
50  * Returns: -ENOMEM if buffer is too small,
51  * -EINVAL if attr.group is invalid,
52  * -EFAULT if copying to userspace failed,
53  * on success return number of stored interrupts
54  */
55 static int flic_get_all_irqs(KVMS390FLICState *flic,
56                              void *buf, int len)
57 {
58     struct kvm_device_attr attr = {
59         .group = KVM_DEV_FLIC_GET_ALL_IRQS,
60         .addr = (uint64_t) buf,
61         .attr = len,
62     };
63     int rc;
64 
65     rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
66 
67     return rc == -1 ? -errno : rc;
68 }
69 
70 static void flic_enable_pfault(KVMS390FLICState *flic)
71 {
72     struct kvm_device_attr attr = {
73         .group = KVM_DEV_FLIC_APF_ENABLE,
74     };
75     int rc;
76 
77     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
78 
79     if (rc) {
80         fprintf(stderr, "flic: couldn't enable pfault\n");
81     }
82 }
83 
84 static void flic_disable_wait_pfault(KVMS390FLICState *flic)
85 {
86     struct kvm_device_attr attr = {
87         .group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
88     };
89     int rc;
90 
91     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
92 
93     if (rc) {
94         fprintf(stderr, "flic: couldn't disable pfault\n");
95     }
96 }
97 
98 /** flic_enqueue_irqs - returns 0 on success
99  * @buf: pointer to buffer which is passed to kernel
100  * @len: length of buffer
101  * @flic: pointer to flic device state
102  *
103  * Returns: -EINVAL if attr.group is unknown
104  */
105 static int flic_enqueue_irqs(void *buf, uint64_t len,
106                             KVMS390FLICState *flic)
107 {
108     int rc;
109     struct kvm_device_attr attr = {
110         .group = KVM_DEV_FLIC_ENQUEUE,
111         .addr = (uint64_t) buf,
112         .attr = len,
113     };
114 
115     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
116 
117     return rc ? -errno : 0;
118 }
119 
120 /**
121  * __get_all_irqs - store all pending irqs in buffer
122  * @flic: pointer to flic device state
123  * @buf: pointer to pointer to a buffer
124  * @len: length of buffer
125  *
126  * Returns: return value of flic_get_all_irqs
127  * Note: Retry and increase buffer size until flic_get_all_irqs
128  * either returns a value >= 0 or a negative error code.
129  * -ENOMEM is an exception, which means the buffer is too small
130  * and we should try again. Other negative error codes can be
131  * -EFAULT and -EINVAL which we ignore at this point
132  */
133 static int __get_all_irqs(KVMS390FLICState *flic,
134                           void **buf, int len)
135 {
136     int r;
137 
138     do {
139         /* returns -ENOMEM if buffer is too small and number
140          * of queued interrupts on success */
141         r = flic_get_all_irqs(flic, *buf, len);
142         if (r >= 0) {
143             break;
144         }
145         len *= 2;
146         *buf = g_try_realloc(*buf, len);
147         if (!buf) {
148             return -ENOMEM;
149         }
150     } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
151 
152     return r;
153 }
154 
155 static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
156                                         uint8_t isc, bool swap,
157                                         bool is_maskable)
158 {
159     struct kvm_s390_io_adapter adapter = {
160         .id = id,
161         .isc = isc,
162         .maskable = is_maskable,
163         .swap = swap,
164     };
165     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
166     int r, ret;
167     struct kvm_device_attr attr = {
168         .group = KVM_DEV_FLIC_ADAPTER_REGISTER,
169         .addr = (uint64_t)&adapter,
170     };
171 
172     if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
173         return -ENOSYS;
174     }
175 
176     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
177 
178     ret = r ? -errno : 0;
179     return ret;
180 }
181 
182 static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
183                                    uint64_t map_addr, bool do_map)
184 {
185     struct kvm_s390_io_adapter_req req = {
186         .id = id,
187         .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP,
188         .addr = map_addr,
189     };
190     struct kvm_device_attr attr = {
191         .group = KVM_DEV_FLIC_ADAPTER_MODIFY,
192         .addr = (uint64_t)&req,
193     };
194     KVMS390FLICState *flic = KVM_S390_FLIC(fs);
195     int r;
196 
197     if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
198         return -ENOSYS;
199     }
200 
201     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
202     return r ? -errno : 0;
203 }
204 
205 static int kvm_s390_add_adapter_routes(S390FLICState *fs,
206                                        AdapterRoutes *routes)
207 {
208     int ret, i;
209     uint64_t ind_offset = routes->adapter.ind_offset;
210 
211     for (i = 0; i < routes->num_routes; i++) {
212         ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter);
213         if (ret < 0) {
214             goto out_undo;
215         }
216         routes->gsi[i] = ret;
217         routes->adapter.ind_offset++;
218     }
219     /* Restore passed-in structure to original state. */
220     routes->adapter.ind_offset = ind_offset;
221     return 0;
222 out_undo:
223     while (--i >= 0) {
224         kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
225         routes->gsi[i] = -1;
226     }
227     routes->adapter.ind_offset = ind_offset;
228     return ret;
229 }
230 
231 static void kvm_s390_release_adapter_routes(S390FLICState *fs,
232                                             AdapterRoutes *routes)
233 {
234     int i;
235 
236     for (i = 0; i < routes->num_routes; i++) {
237         if (routes->gsi[i] >= 0) {
238             kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
239             routes->gsi[i] = -1;
240         }
241     }
242 }
243 
244 /**
245  * kvm_flic_save - Save pending floating interrupts
246  * @f: QEMUFile containing migration state
247  * @opaque: pointer to flic device state
248  *
249  * Note: Pass buf and len to kernel. Start with one page and
250  * increase until buffer is sufficient or maxium size is
251  * reached
252  */
253 static void kvm_flic_save(QEMUFile *f, void *opaque)
254 {
255     KVMS390FLICState *flic = opaque;
256     int len = FLIC_SAVE_INITIAL_SIZE;
257     void *buf;
258     int count;
259 
260     flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
261 
262     buf = g_try_malloc0(len);
263     if (!buf) {
264         /* Storing FLIC_FAILED into the count field here will cause the
265          * target system to fail when attempting to load irqs from the
266          * migration state */
267         error_report("flic: couldn't allocate memory");
268         qemu_put_be64(f, FLIC_FAILED);
269         return;
270     }
271 
272     count = __get_all_irqs(flic, &buf, len);
273     if (count < 0) {
274         error_report("flic: couldn't retrieve irqs from kernel, rc %d",
275                      count);
276         /* Storing FLIC_FAILED into the count field here will cause the
277          * target system to fail when attempting to load irqs from the
278          * migration state */
279         qemu_put_be64(f, FLIC_FAILED);
280     } else {
281         qemu_put_be64(f, count);
282         qemu_put_buffer(f, (uint8_t *) buf,
283                         count * sizeof(struct kvm_s390_irq));
284     }
285     g_free(buf);
286 }
287 
288 /**
289  * kvm_flic_load - Load pending floating interrupts
290  * @f: QEMUFile containing migration state
291  * @opaque: pointer to flic device state
292  * @version_id: version id for migration
293  *
294  * Returns: value of flic_enqueue_irqs, -EINVAL on error
295  * Note: Do nothing when no interrupts where stored
296  * in QEMUFile
297  */
298 static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
299 {
300     uint64_t len = 0;
301     uint64_t count = 0;
302     void *buf = NULL;
303     int r = 0;
304 
305     if (version_id != FLIC_SAVEVM_VERSION) {
306         r = -EINVAL;
307         goto out;
308     }
309 
310     flic_enable_pfault((struct KVMS390FLICState *) opaque);
311 
312     count = qemu_get_be64(f);
313     len = count * sizeof(struct kvm_s390_irq);
314     if (count == FLIC_FAILED) {
315         r = -EINVAL;
316         goto out;
317     }
318     if (count == 0) {
319         r = 0;
320         goto out;
321     }
322     buf = g_try_malloc0(len);
323     if (!buf) {
324         r = -ENOMEM;
325         goto out;
326     }
327 
328     if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
329         r = -EINVAL;
330         goto out_free;
331     }
332     r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
333 
334 out_free:
335     g_free(buf);
336 out:
337     return r;
338 }
339 
340 static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
341 {
342     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
343     struct kvm_create_device cd = {0};
344     int ret;
345 
346     flic_state->fd = -1;
347     if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
348         trace_flic_no_device_api(errno);
349         return;
350     }
351 
352     cd.type = KVM_DEV_TYPE_FLIC;
353     ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
354     if (ret < 0) {
355         trace_flic_create_device(errno);
356         return;
357     }
358     flic_state->fd = cd.fd;
359 
360     /* Register savevm handler for floating interrupts */
361     register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
362                     kvm_flic_load, (void *) flic_state);
363 }
364 
365 static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
366 {
367     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
368 
369     unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
370 }
371 
372 static void kvm_s390_flic_reset(DeviceState *dev)
373 {
374     KVMS390FLICState *flic = KVM_S390_FLIC(dev);
375     struct kvm_device_attr attr = {
376         .group = KVM_DEV_FLIC_CLEAR_IRQS,
377     };
378     int rc = 0;
379 
380     if (flic->fd == -1) {
381         return;
382     }
383 
384     flic_disable_wait_pfault(flic);
385 
386     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
387     if (rc) {
388         trace_flic_reset_failed(errno);
389     }
390 
391     flic_enable_pfault(flic);
392 }
393 
394 static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
395 {
396     DeviceClass *dc = DEVICE_CLASS(oc);
397     S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
398 
399     dc->realize = kvm_s390_flic_realize;
400     dc->unrealize = kvm_s390_flic_unrealize;
401     dc->reset = kvm_s390_flic_reset;
402     fsc->register_io_adapter = kvm_s390_register_io_adapter;
403     fsc->io_adapter_map = kvm_s390_io_adapter_map;
404     fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
405     fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
406 }
407 
408 static const TypeInfo kvm_s390_flic_info = {
409     .name          = TYPE_KVM_S390_FLIC,
410     .parent        = TYPE_S390_FLIC_COMMON,
411     .instance_size = sizeof(KVMS390FLICState),
412     .class_init    = kvm_s390_flic_class_init,
413 };
414 
415 static void kvm_s390_flic_register_types(void)
416 {
417     type_register_static(&kvm_s390_flic_info);
418 }
419 
420 type_init(kvm_s390_flic_register_types)
421