xref: /openbmc/qemu/hw/intc/s390_flic.c (revision 3a553fc65826e0e682ed0fff770ad0d421c6d407)
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