s390x/kvm: implement floating-interrupt controller device
[qemu.git] / hw / intc / s390_flic.c
blob1a7876da7873fb007d523eff3f1a2054915d67f7
1 /*
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
9 * directory.
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"
20 #define FLIC_SAVE_INITIAL_SIZE getpagesize()
21 #define FLIC_FAILED (-1UL)
22 #define FLIC_SAVEVM_VERSION 1
24 void s390_flic_init(void)
26 DeviceState *dev;
27 int r;
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");
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
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,
52 void *buf, int len)
54 struct kvm_device_attr attr = {
55 .group = KVM_DEV_FLIC_GET_ALL_IRQS,
56 .addr = (uint64_t) buf,
57 .attr = len,
59 int rc;
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)
76 int rc;
77 struct kvm_device_attr attr = {
78 .group = KVM_DEV_FLIC_ENQUEUE,
79 .addr = (uint64_t) buf,
80 .attr = len,
83 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
85 return rc ? -errno : 0;
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
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,
102 void **buf, int len)
104 int r;
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;
113 len *= 2;
114 *buf = g_try_realloc(*buf, len);
115 if (!buf) {
116 return -ENOMEM;
118 } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
120 return r;
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
130 * reached
132 static void kvm_flic_save(QEMUFile *f, void *opaque)
134 KVMS390FLICState *flic = opaque;
135 int len = FLIC_SAVE_INITIAL_SIZE;
136 void *buf;
137 int count;
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;
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));
162 g_free(buf);
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
173 * in QEMUFile
175 static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
177 uint64_t len = 0;
178 uint64_t count = 0;
179 void *buf = NULL;
180 int r = 0;
182 if (version_id != FLIC_SAVEVM_VERSION) {
183 r = -EINVAL;
184 goto out;
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;
193 if (count == 0) {
194 r = 0;
195 goto out;
197 buf = g_try_malloc0(len);
198 if (!buf) {
199 r = -ENOMEM;
200 goto out;
203 if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
204 r = -EINVAL;
205 goto out_free;
207 r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
209 out_free:
210 g_free(buf);
211 out:
212 return r;
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};
219 int ret;
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;
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;
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,
253 int rc = 0;
255 if (flic->fd == -1) {
256 return;
259 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
260 if (rc) {
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)