2 * QEMU S390x KVM floating interrupt controller (flic)
4 * Copyright 2014 IBM Corp.
5 * Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
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
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"
20 #define FLIC_SAVE_INITIAL_SIZE getpagesize()
21 #define FLIC_FAILED (-1UL)
22 #define FLIC_SAVEVM_VERSION 1
24 void s390_flic_init(void)
30 dev
= qdev_create(NULL
, "s390-flic");
31 object_property_add_child(qdev_get_machine(), "s390-flic",
35 error_report("flic: couldn't create qdev");
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
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
51 static int flic_get_all_irqs(KVMS390FLICState
*flic
,
54 struct kvm_device_attr attr
= {
55 .group
= KVM_DEV_FLIC_GET_ALL_IRQS
,
56 .addr
= (uint64_t) buf
,
61 rc
= ioctl(flic
->fd
, KVM_GET_DEVICE_ATTR
, &attr
);
63 return rc
== -1 ? -errno
: rc
;
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
71 * Returns: -EINVAL if attr.group is unknown
73 static int flic_enqueue_irqs(void *buf
, uint64_t len
,
74 KVMS390FLICState
*flic
)
77 struct kvm_device_attr attr
= {
78 .group
= KVM_DEV_FLIC_ENQUEUE
,
79 .addr
= (uint64_t) buf
,
83 rc
= ioctl(flic
->fd
, KVM_SET_DEVICE_ATTR
, &attr
);
85 return rc
? -errno
: 0;
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
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
101 static int __get_all_irqs(KVMS390FLICState
*flic
,
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
);
114 *buf
= g_try_realloc(*buf
, len
);
118 } while (r
== -ENOMEM
&& len
<= KVM_S390_FLIC_MAX_BUFFER
);
124 * kvm_flic_save - Save pending floating interrupts
125 * @f: QEMUFile containing migration state
126 * @opaque: pointer to flic device state
128 * Note: Pass buf and len to kernel. Start with one page and
129 * increase until buffer is sufficient or maxium size is
132 static void kvm_flic_save(QEMUFile
*f
, void *opaque
)
134 KVMS390FLICState
*flic
= opaque
;
135 int len
= FLIC_SAVE_INITIAL_SIZE
;
139 buf
= g_try_malloc0(len
);
141 /* Storing FLIC_FAILED into the count field here will cause the
142 * target system to fail when attempting to load irqs from the
144 error_report("flic: couldn't allocate memory");
145 qemu_put_be64(f
, FLIC_FAILED
);
149 count
= __get_all_irqs(flic
, &buf
, len
);
151 error_report("flic: couldn't retrieve irqs from kernel, rc %d",
153 /* Storing FLIC_FAILED into the count field here will cause the
154 * target system to fail when attempting to load irqs from the
156 qemu_put_be64(f
, FLIC_FAILED
);
158 qemu_put_be64(f
, count
);
159 qemu_put_buffer(f
, (uint8_t *) buf
,
160 count
* sizeof(struct kvm_s390_irq
));
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
171 * Returns: value of flic_enqueue_irqs, -EINVAL on error
172 * Note: Do nothing when no interrupts where stored
175 static int kvm_flic_load(QEMUFile
*f
, void *opaque
, int version_id
)
182 if (version_id
!= FLIC_SAVEVM_VERSION
) {
187 count
= qemu_get_be64(f
);
188 len
= count
* sizeof(struct kvm_s390_irq
);
189 if (count
== FLIC_FAILED
) {
197 buf
= g_try_malloc0(len
);
203 if (qemu_get_buffer(f
, (uint8_t *) buf
, len
) != len
) {
207 r
= flic_enqueue_irqs(buf
, len
, (struct KVMS390FLICState
*) opaque
);
215 static void kvm_s390_flic_realize(DeviceState
*dev
, Error
**errp
)
217 KVMS390FLICState
*flic_state
= KVM_S390_FLIC(dev
);
218 struct kvm_create_device cd
= {0};
222 if (!kvm_check_extension(kvm_state
, KVM_CAP_DEVICE_CTRL
)) {
223 trace_flic_no_device_api(errno
);
227 cd
.type
= KVM_DEV_TYPE_FLIC
;
228 ret
= kvm_vm_ioctl(kvm_state
, KVM_CREATE_DEVICE
, &cd
);
230 trace_flic_create_device(errno
);
233 flic_state
->fd
= cd
.fd
;
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
);
240 static void kvm_s390_flic_unrealize(DeviceState
*dev
, Error
**errp
)
242 KVMS390FLICState
*flic_state
= KVM_S390_FLIC(dev
);
244 unregister_savevm(DEVICE(flic_state
), "s390-flic", flic_state
);
247 static void kvm_s390_flic_reset(DeviceState
*dev
)
249 KVMS390FLICState
*flic
= KVM_S390_FLIC(dev
);
250 struct kvm_device_attr attr
= {
251 .group
= KVM_DEV_FLIC_CLEAR_IRQS
,
255 if (flic
->fd
== -1) {
259 rc
= ioctl(flic
->fd
, KVM_SET_DEVICE_ATTR
, &attr
);
261 trace_flic_reset_failed(errno
);
265 static void kvm_s390_flic_class_init(ObjectClass
*oc
, void *data
)
267 DeviceClass
*dc
= DEVICE_CLASS(oc
);
269 dc
->realize
= kvm_s390_flic_realize
;
270 dc
->unrealize
= kvm_s390_flic_unrealize
;
271 dc
->reset
= kvm_s390_flic_reset
;
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
,
281 static void kvm_s390_flic_register_types(void)
283 type_register_static(&kvm_s390_flic_info
);
286 type_init(kvm_s390_flic_register_types
)