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