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