]> git.proxmox.com Git - mirror_qemu.git/blame - hw/intc/s390_flic_kvm.c
s390x/virtio-ccw: wire up irq routing and irqfds
[mirror_qemu.git] / hw / intc / s390_flic_kvm.c
CommitLineData
7b35d0c4
CH
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 * Cornelia Huck <cornelia.huck@de.ibm.com>
7 *
8 * This work is licensed under the terms of the GNU GPL, version 2 or (at
9 * your option) any later version. See the COPYING file in the top-level
10 * directory.
11 */
12
13#include <sys/ioctl.h>
14#include "qemu/error-report.h"
15#include "hw/sysbus.h"
16#include "sysemu/kvm.h"
17#include "migration/qemu-file.h"
18#include "hw/s390x/s390_flic.h"
d426d9fb 19#include "hw/s390x/adapter.h"
7b35d0c4
CH
20#include "trace.h"
21
22#define FLIC_SAVE_INITIAL_SIZE getpagesize()
23#define FLIC_FAILED (-1UL)
24#define FLIC_SAVEVM_VERSION 1
25
26typedef struct KVMS390FLICState {
27 S390FLICState parent_obj;
28
29 uint32_t fd;
30} KVMS390FLICState;
31
32DeviceState *s390_flic_kvm_create(void)
33{
34 DeviceState *dev = NULL;
35
36 if (kvm_enabled()) {
37 dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
38 object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
39 OBJECT(dev), NULL);
40 }
41 return dev;
42}
43
44/**
45 * flic_get_all_irqs - store all pending irqs in buffer
46 * @buf: pointer to buffer which is passed to kernel
47 * @len: length of buffer
48 * @flic: pointer to flic device state
49 *
50 * Returns: -ENOMEM if buffer is too small,
51 * -EINVAL if attr.group is invalid,
52 * -EFAULT if copying to userspace failed,
53 * on success return number of stored interrupts
54 */
55static int flic_get_all_irqs(KVMS390FLICState *flic,
56 void *buf, int len)
57{
58 struct kvm_device_attr attr = {
59 .group = KVM_DEV_FLIC_GET_ALL_IRQS,
60 .addr = (uint64_t) buf,
61 .attr = len,
62 };
63 int rc;
64
65 rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
66
67 return rc == -1 ? -errno : rc;
68}
69
70static void flic_enable_pfault(KVMS390FLICState *flic)
71{
72 struct kvm_device_attr attr = {
73 .group = KVM_DEV_FLIC_APF_ENABLE,
74 };
75 int rc;
76
77 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
78
79 if (rc) {
80 fprintf(stderr, "flic: couldn't enable pfault\n");
81 }
82}
83
84static void flic_disable_wait_pfault(KVMS390FLICState *flic)
85{
86 struct kvm_device_attr attr = {
87 .group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
88 };
89 int rc;
90
91 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
92
93 if (rc) {
94 fprintf(stderr, "flic: couldn't disable pfault\n");
95 }
96}
97
98/** flic_enqueue_irqs - returns 0 on success
99 * @buf: pointer to buffer which is passed to kernel
100 * @len: length of buffer
101 * @flic: pointer to flic device state
102 *
103 * Returns: -EINVAL if attr.group is unknown
104 */
105static int flic_enqueue_irqs(void *buf, uint64_t len,
106 KVMS390FLICState *flic)
107{
108 int rc;
109 struct kvm_device_attr attr = {
110 .group = KVM_DEV_FLIC_ENQUEUE,
111 .addr = (uint64_t) buf,
112 .attr = len,
113 };
114
115 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
116
117 return rc ? -errno : 0;
118}
119
120/**
121 * __get_all_irqs - store all pending irqs in buffer
122 * @flic: pointer to flic device state
123 * @buf: pointer to pointer to a buffer
124 * @len: length of buffer
125 *
126 * Returns: return value of flic_get_all_irqs
127 * Note: Retry and increase buffer size until flic_get_all_irqs
128 * either returns a value >= 0 or a negative error code.
129 * -ENOMEM is an exception, which means the buffer is too small
130 * and we should try again. Other negative error codes can be
131 * -EFAULT and -EINVAL which we ignore at this point
132 */
133static int __get_all_irqs(KVMS390FLICState *flic,
134 void **buf, int len)
135{
136 int r;
137
138 do {
139 /* returns -ENOMEM if buffer is too small and number
140 * of queued interrupts on success */
141 r = flic_get_all_irqs(flic, *buf, len);
142 if (r >= 0) {
143 break;
144 }
145 len *= 2;
146 *buf = g_try_realloc(*buf, len);
147 if (!buf) {
148 return -ENOMEM;
149 }
150 } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
151
152 return r;
153}
154
03cf077a
CH
155static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
156 uint8_t isc, bool swap,
157 bool is_maskable)
158{
159 struct kvm_s390_io_adapter adapter = {
160 .id = id,
161 .isc = isc,
162 .maskable = is_maskable,
163 .swap = swap,
164 };
165 KVMS390FLICState *flic = KVM_S390_FLIC(fs);
166 int r, ret;
167 struct kvm_device_attr attr = {
168 .group = KVM_DEV_FLIC_ADAPTER_REGISTER,
169 .addr = (uint64_t)&adapter,
170 };
171
172 if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
173 return -ENOSYS;
174 }
175
176 r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
177
178 ret = r ? -errno : 0;
179 return ret;
180}
181
d426d9fb
CH
182static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
183 uint64_t map_addr, bool do_map)
184{
185 struct kvm_s390_io_adapter_req req = {
186 .id = id,
187 .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP,
188 .addr = map_addr,
189 };
190 struct kvm_device_attr attr = {
191 .group = KVM_DEV_FLIC_ADAPTER_MODIFY,
192 .addr = (uint64_t)&req,
193 };
194 KVMS390FLICState *flic = KVM_S390_FLIC(fs);
195 int r;
196
197 if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
198 return -ENOSYS;
199 }
200
201 r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
202 return r ? -errno : 0;
203}
204
205static int kvm_s390_add_adapter_routes(S390FLICState *fs,
206 AdapterRoutes *routes)
207{
208 int ret, i;
209 uint64_t ind_offset = routes->adapter.ind_offset;
210
211 for (i = 0; i < routes->num_routes; i++) {
212 ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter);
213 if (ret < 0) {
214 goto out_undo;
215 }
216 routes->gsi[i] = ret;
217 routes->adapter.ind_offset++;
218 }
219 /* Restore passed-in structure to original state. */
220 routes->adapter.ind_offset = ind_offset;
221 return 0;
222out_undo:
223 while (--i >= 0) {
224 kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
225 routes->gsi[i] = -1;
226 }
227 routes->adapter.ind_offset = ind_offset;
228 return ret;
229}
230
231static void kvm_s390_release_adapter_routes(S390FLICState *fs,
232 AdapterRoutes *routes)
233{
234 int i;
235
236 for (i = 0; i < routes->num_routes; i++) {
237 if (routes->gsi[i] >= 0) {
238 kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
239 routes->gsi[i] = -1;
240 }
241 }
242}
243
7b35d0c4
CH
244/**
245 * kvm_flic_save - Save pending floating interrupts
246 * @f: QEMUFile containing migration state
247 * @opaque: pointer to flic device state
248 *
249 * Note: Pass buf and len to kernel. Start with one page and
250 * increase until buffer is sufficient or maxium size is
251 * reached
252 */
253static void kvm_flic_save(QEMUFile *f, void *opaque)
254{
255 KVMS390FLICState *flic = opaque;
256 int len = FLIC_SAVE_INITIAL_SIZE;
257 void *buf;
258 int count;
259
260 flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
261
262 buf = g_try_malloc0(len);
263 if (!buf) {
264 /* Storing FLIC_FAILED into the count field here will cause the
265 * target system to fail when attempting to load irqs from the
266 * migration state */
267 error_report("flic: couldn't allocate memory");
268 qemu_put_be64(f, FLIC_FAILED);
269 return;
270 }
271
272 count = __get_all_irqs(flic, &buf, len);
273 if (count < 0) {
274 error_report("flic: couldn't retrieve irqs from kernel, rc %d",
275 count);
276 /* Storing FLIC_FAILED into the count field here will cause the
277 * target system to fail when attempting to load irqs from the
278 * migration state */
279 qemu_put_be64(f, FLIC_FAILED);
280 } else {
281 qemu_put_be64(f, count);
282 qemu_put_buffer(f, (uint8_t *) buf,
283 count * sizeof(struct kvm_s390_irq));
284 }
285 g_free(buf);
286}
287
288/**
289 * kvm_flic_load - Load pending floating interrupts
290 * @f: QEMUFile containing migration state
291 * @opaque: pointer to flic device state
292 * @version_id: version id for migration
293 *
294 * Returns: value of flic_enqueue_irqs, -EINVAL on error
295 * Note: Do nothing when no interrupts where stored
296 * in QEMUFile
297 */
298static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
299{
300 uint64_t len = 0;
301 uint64_t count = 0;
302 void *buf = NULL;
303 int r = 0;
304
305 if (version_id != FLIC_SAVEVM_VERSION) {
306 r = -EINVAL;
307 goto out;
308 }
309
310 flic_enable_pfault((struct KVMS390FLICState *) opaque);
311
312 count = qemu_get_be64(f);
313 len = count * sizeof(struct kvm_s390_irq);
314 if (count == FLIC_FAILED) {
315 r = -EINVAL;
316 goto out;
317 }
318 if (count == 0) {
319 r = 0;
320 goto out;
321 }
322 buf = g_try_malloc0(len);
323 if (!buf) {
324 r = -ENOMEM;
325 goto out;
326 }
327
328 if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
329 r = -EINVAL;
330 goto out_free;
331 }
332 r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
333
334out_free:
335 g_free(buf);
336out:
337 return r;
338}
339
340static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
341{
342 KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
343 struct kvm_create_device cd = {0};
344 int ret;
345
346 flic_state->fd = -1;
347 if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
348 trace_flic_no_device_api(errno);
349 return;
350 }
351
352 cd.type = KVM_DEV_TYPE_FLIC;
353 ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
354 if (ret < 0) {
355 trace_flic_create_device(errno);
356 return;
357 }
358 flic_state->fd = cd.fd;
359
360 /* Register savevm handler for floating interrupts */
361 register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
362 kvm_flic_load, (void *) flic_state);
363}
364
365static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
366{
367 KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
368
369 unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
370}
371
372static void kvm_s390_flic_reset(DeviceState *dev)
373{
374 KVMS390FLICState *flic = KVM_S390_FLIC(dev);
375 struct kvm_device_attr attr = {
376 .group = KVM_DEV_FLIC_CLEAR_IRQS,
377 };
378 int rc = 0;
379
380 if (flic->fd == -1) {
381 return;
382 }
383
384 flic_disable_wait_pfault(flic);
385
386 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
387 if (rc) {
388 trace_flic_reset_failed(errno);
389 }
390
391 flic_enable_pfault(flic);
392}
393
394static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
395{
396 DeviceClass *dc = DEVICE_CLASS(oc);
03cf077a 397 S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
7b35d0c4
CH
398
399 dc->realize = kvm_s390_flic_realize;
400 dc->unrealize = kvm_s390_flic_unrealize;
401 dc->reset = kvm_s390_flic_reset;
03cf077a 402 fsc->register_io_adapter = kvm_s390_register_io_adapter;
d426d9fb
CH
403 fsc->io_adapter_map = kvm_s390_io_adapter_map;
404 fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
405 fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
7b35d0c4
CH
406}
407
408static const TypeInfo kvm_s390_flic_info = {
409 .name = TYPE_KVM_S390_FLIC,
410 .parent = TYPE_S390_FLIC_COMMON,
411 .instance_size = sizeof(KVMS390FLICState),
412 .class_init = kvm_s390_flic_class_init,
413};
414
415static void kvm_s390_flic_register_types(void)
416{
417 type_register_static(&kvm_s390_flic_info);
418}
419
420type_init(kvm_s390_flic_register_types)