xref: /qemu/hw/intc/s390_flic_kvm.c (revision 7b35d0c44cae3dcce6347a0729a416c2929cd4bb)
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 "trace.h"
20 
21 #define FLIC_SAVE_INITIAL_SIZE getpagesize()
22 #define FLIC_FAILED (-1UL)
23 #define FLIC_SAVEVM_VERSION 1
24 
25 typedef struct KVMS390FLICState {
26     S390FLICState parent_obj;
27 
28     uint32_t fd;
29 } KVMS390FLICState;
30 
31 DeviceState *s390_flic_kvm_create(void)
32 {
33     DeviceState *dev = NULL;
34 
35     if (kvm_enabled()) {
36         dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
37         object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
38                                   OBJECT(dev), NULL);
39     }
40     return dev;
41 }
42 
43 /**
44  * flic_get_all_irqs - store all pending irqs in buffer
45  * @buf: pointer to buffer which is passed to kernel
46  * @len: length of buffer
47  * @flic: pointer to flic device state
48  *
49  * Returns: -ENOMEM if buffer is too small,
50  * -EINVAL if attr.group is invalid,
51  * -EFAULT if copying to userspace failed,
52  * on success return number of stored interrupts
53  */
54 static int flic_get_all_irqs(KVMS390FLICState *flic,
55                              void *buf, int len)
56 {
57     struct kvm_device_attr attr = {
58         .group = KVM_DEV_FLIC_GET_ALL_IRQS,
59         .addr = (uint64_t) buf,
60         .attr = len,
61     };
62     int rc;
63 
64     rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
65 
66     return rc == -1 ? -errno : rc;
67 }
68 
69 static void flic_enable_pfault(KVMS390FLICState *flic)
70 {
71     struct kvm_device_attr attr = {
72         .group = KVM_DEV_FLIC_APF_ENABLE,
73     };
74     int rc;
75 
76     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
77 
78     if (rc) {
79         fprintf(stderr, "flic: couldn't enable pfault\n");
80     }
81 }
82 
83 static void flic_disable_wait_pfault(KVMS390FLICState *flic)
84 {
85     struct kvm_device_attr attr = {
86         .group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
87     };
88     int rc;
89 
90     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
91 
92     if (rc) {
93         fprintf(stderr, "flic: couldn't disable pfault\n");
94     }
95 }
96 
97 /** flic_enqueue_irqs - returns 0 on success
98  * @buf: pointer to buffer which is passed to kernel
99  * @len: length of buffer
100  * @flic: pointer to flic device state
101  *
102  * Returns: -EINVAL if attr.group is unknown
103  */
104 static int flic_enqueue_irqs(void *buf, uint64_t len,
105                             KVMS390FLICState *flic)
106 {
107     int rc;
108     struct kvm_device_attr attr = {
109         .group = KVM_DEV_FLIC_ENQUEUE,
110         .addr = (uint64_t) buf,
111         .attr = len,
112     };
113 
114     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
115 
116     return rc ? -errno : 0;
117 }
118 
119 /**
120  * __get_all_irqs - store all pending irqs in buffer
121  * @flic: pointer to flic device state
122  * @buf: pointer to pointer to a buffer
123  * @len: length of buffer
124  *
125  * Returns: return value of flic_get_all_irqs
126  * Note: Retry and increase buffer size until flic_get_all_irqs
127  * either returns a value >= 0 or a negative error code.
128  * -ENOMEM is an exception, which means the buffer is too small
129  * and we should try again. Other negative error codes can be
130  * -EFAULT and -EINVAL which we ignore at this point
131  */
132 static int __get_all_irqs(KVMS390FLICState *flic,
133                           void **buf, int len)
134 {
135     int r;
136 
137     do {
138         /* returns -ENOMEM if buffer is too small and number
139          * of queued interrupts on success */
140         r = flic_get_all_irqs(flic, *buf, len);
141         if (r >= 0) {
142             break;
143         }
144         len *= 2;
145         *buf = g_try_realloc(*buf, len);
146         if (!buf) {
147             return -ENOMEM;
148         }
149     } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
150 
151     return r;
152 }
153 
154 /**
155  * kvm_flic_save - Save pending floating interrupts
156  * @f: QEMUFile containing migration state
157  * @opaque: pointer to flic device state
158  *
159  * Note: Pass buf and len to kernel. Start with one page and
160  * increase until buffer is sufficient or maxium size is
161  * reached
162  */
163 static void kvm_flic_save(QEMUFile *f, void *opaque)
164 {
165     KVMS390FLICState *flic = opaque;
166     int len = FLIC_SAVE_INITIAL_SIZE;
167     void *buf;
168     int count;
169 
170     flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
171 
172     buf = g_try_malloc0(len);
173     if (!buf) {
174         /* Storing FLIC_FAILED into the count field here will cause the
175          * target system to fail when attempting to load irqs from the
176          * migration state */
177         error_report("flic: couldn't allocate memory");
178         qemu_put_be64(f, FLIC_FAILED);
179         return;
180     }
181 
182     count = __get_all_irqs(flic, &buf, len);
183     if (count < 0) {
184         error_report("flic: couldn't retrieve irqs from kernel, rc %d",
185                      count);
186         /* Storing FLIC_FAILED into the count field here will cause the
187          * target system to fail when attempting to load irqs from the
188          * migration state */
189         qemu_put_be64(f, FLIC_FAILED);
190     } else {
191         qemu_put_be64(f, count);
192         qemu_put_buffer(f, (uint8_t *) buf,
193                         count * sizeof(struct kvm_s390_irq));
194     }
195     g_free(buf);
196 }
197 
198 /**
199  * kvm_flic_load - Load pending floating interrupts
200  * @f: QEMUFile containing migration state
201  * @opaque: pointer to flic device state
202  * @version_id: version id for migration
203  *
204  * Returns: value of flic_enqueue_irqs, -EINVAL on error
205  * Note: Do nothing when no interrupts where stored
206  * in QEMUFile
207  */
208 static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
209 {
210     uint64_t len = 0;
211     uint64_t count = 0;
212     void *buf = NULL;
213     int r = 0;
214 
215     if (version_id != FLIC_SAVEVM_VERSION) {
216         r = -EINVAL;
217         goto out;
218     }
219 
220     flic_enable_pfault((struct KVMS390FLICState *) opaque);
221 
222     count = qemu_get_be64(f);
223     len = count * sizeof(struct kvm_s390_irq);
224     if (count == FLIC_FAILED) {
225         r = -EINVAL;
226         goto out;
227     }
228     if (count == 0) {
229         r = 0;
230         goto out;
231     }
232     buf = g_try_malloc0(len);
233     if (!buf) {
234         r = -ENOMEM;
235         goto out;
236     }
237 
238     if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
239         r = -EINVAL;
240         goto out_free;
241     }
242     r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
243 
244 out_free:
245     g_free(buf);
246 out:
247     return r;
248 }
249 
250 static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
251 {
252     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
253     struct kvm_create_device cd = {0};
254     int ret;
255 
256     flic_state->fd = -1;
257     if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
258         trace_flic_no_device_api(errno);
259         return;
260     }
261 
262     cd.type = KVM_DEV_TYPE_FLIC;
263     ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
264     if (ret < 0) {
265         trace_flic_create_device(errno);
266         return;
267     }
268     flic_state->fd = cd.fd;
269 
270     /* Register savevm handler for floating interrupts */
271     register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
272                     kvm_flic_load, (void *) flic_state);
273 }
274 
275 static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
276 {
277     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
278 
279     unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
280 }
281 
282 static void kvm_s390_flic_reset(DeviceState *dev)
283 {
284     KVMS390FLICState *flic = KVM_S390_FLIC(dev);
285     struct kvm_device_attr attr = {
286         .group = KVM_DEV_FLIC_CLEAR_IRQS,
287     };
288     int rc = 0;
289 
290     if (flic->fd == -1) {
291         return;
292     }
293 
294     flic_disable_wait_pfault(flic);
295 
296     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
297     if (rc) {
298         trace_flic_reset_failed(errno);
299     }
300 
301     flic_enable_pfault(flic);
302 }
303 
304 static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
305 {
306     DeviceClass *dc = DEVICE_CLASS(oc);
307 
308     dc->realize = kvm_s390_flic_realize;
309     dc->unrealize = kvm_s390_flic_unrealize;
310     dc->reset = kvm_s390_flic_reset;
311 }
312 
313 static const TypeInfo kvm_s390_flic_info = {
314     .name          = TYPE_KVM_S390_FLIC,
315     .parent        = TYPE_S390_FLIC_COMMON,
316     .instance_size = sizeof(KVMS390FLICState),
317     .class_init    = kvm_s390_flic_class_init,
318 };
319 
320 static void kvm_s390_flic_register_types(void)
321 {
322     type_register_static(&kvm_s390_flic_info);
323 }
324 
325 type_init(kvm_s390_flic_register_types)
326