1
2
3
4
5
6
7
8
9
10
11
12
13#include "qemu/osdep.h"
14#include <sys/ioctl.h>
15#include "qemu/error-report.h"
16#include "hw/sysbus.h"
17#include "sysemu/kvm.h"
18#include "migration/qemu-file.h"
19#include "hw/s390x/s390_flic.h"
20#include "hw/s390x/adapter.h"
21#include "trace.h"
22
23#define FLIC_SAVE_INITIAL_SIZE getpagesize()
24#define FLIC_FAILED (-1UL)
25#define FLIC_SAVEVM_VERSION 1
26
27typedef struct KVMS390FLICState {
28 S390FLICState parent_obj;
29
30 uint32_t fd;
31} KVMS390FLICState;
32
33DeviceState *s390_flic_kvm_create(void)
34{
35 DeviceState *dev = NULL;
36
37 if (kvm_enabled()) {
38 dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
39 object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
40 OBJECT(dev), NULL);
41 }
42 return dev;
43}
44
45
46
47
48
49
50
51
52
53
54
55
56static int flic_get_all_irqs(KVMS390FLICState *flic,
57 void *buf, int len)
58{
59 struct kvm_device_attr attr = {
60 .group = KVM_DEV_FLIC_GET_ALL_IRQS,
61 .addr = (uint64_t) buf,
62 .attr = len,
63 };
64 int rc;
65
66 rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
67
68 return rc == -1 ? -errno : rc;
69}
70
71static void flic_enable_pfault(KVMS390FLICState *flic)
72{
73 struct kvm_device_attr attr = {
74 .group = KVM_DEV_FLIC_APF_ENABLE,
75 };
76 int rc;
77
78 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
79
80 if (rc) {
81 fprintf(stderr, "flic: couldn't enable pfault\n");
82 }
83}
84
85static void flic_disable_wait_pfault(KVMS390FLICState *flic)
86{
87 struct kvm_device_attr attr = {
88 .group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
89 };
90 int rc;
91
92 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
93
94 if (rc) {
95 fprintf(stderr, "flic: couldn't disable pfault\n");
96 }
97}
98
99
100
101
102
103
104
105
106static int flic_enqueue_irqs(void *buf, uint64_t len,
107 KVMS390FLICState *flic)
108{
109 int rc;
110 struct kvm_device_attr attr = {
111 .group = KVM_DEV_FLIC_ENQUEUE,
112 .addr = (uint64_t) buf,
113 .attr = len,
114 };
115
116 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
117
118 return rc ? -errno : 0;
119}
120
121int kvm_s390_inject_flic(struct kvm_s390_irq *irq)
122{
123 static KVMS390FLICState *flic;
124
125 if (unlikely(!flic)) {
126 flic = KVM_S390_FLIC(s390_get_flic());
127 }
128 return flic_enqueue_irqs(irq, sizeof(*irq), flic);
129}
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144static int __get_all_irqs(KVMS390FLICState *flic,
145 void **buf, int len)
146{
147 int r;
148
149 do {
150
151
152 r = flic_get_all_irqs(flic, *buf, len);
153 if (r >= 0) {
154 break;
155 }
156 len *= 2;
157 *buf = g_try_realloc(*buf, len);
158 if (!buf) {
159 return -ENOMEM;
160 }
161 } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
162
163 return r;
164}
165
166static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
167 uint8_t isc, bool swap,
168 bool is_maskable)
169{
170 struct kvm_s390_io_adapter adapter = {
171 .id = id,
172 .isc = isc,
173 .maskable = is_maskable,
174 .swap = swap,
175 };
176 KVMS390FLICState *flic = KVM_S390_FLIC(fs);
177 int r, ret;
178 struct kvm_device_attr attr = {
179 .group = KVM_DEV_FLIC_ADAPTER_REGISTER,
180 .addr = (uint64_t)&adapter,
181 };
182
183 if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
184
185 return 0;
186 }
187
188 r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
189
190 ret = r ? -errno : 0;
191 return ret;
192}
193
194static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
195 uint64_t map_addr, bool do_map)
196{
197 struct kvm_s390_io_adapter_req req = {
198 .id = id,
199 .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP,
200 .addr = map_addr,
201 };
202 struct kvm_device_attr attr = {
203 .group = KVM_DEV_FLIC_ADAPTER_MODIFY,
204 .addr = (uint64_t)&req,
205 };
206 KVMS390FLICState *flic = KVM_S390_FLIC(fs);
207 int r;
208
209 if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
210
211 return 0;
212 }
213
214 r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
215 return r ? -errno : 0;
216}
217
218static int kvm_s390_add_adapter_routes(S390FLICState *fs,
219 AdapterRoutes *routes)
220{
221 int ret, i;
222 uint64_t ind_offset = routes->adapter.ind_offset;
223
224 for (i = 0; i < routes->num_routes; i++) {
225 ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter);
226 if (ret < 0) {
227 goto out_undo;
228 }
229 routes->gsi[i] = ret;
230 routes->adapter.ind_offset++;
231 }
232 kvm_irqchip_commit_routes(kvm_state);
233
234
235 routes->adapter.ind_offset = ind_offset;
236 return 0;
237out_undo:
238 while (--i >= 0) {
239 kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
240 routes->gsi[i] = -1;
241 }
242 routes->adapter.ind_offset = ind_offset;
243 return ret;
244}
245
246static void kvm_s390_release_adapter_routes(S390FLICState *fs,
247 AdapterRoutes *routes)
248{
249 int i;
250
251 for (i = 0; i < routes->num_routes; i++) {
252 if (routes->gsi[i] >= 0) {
253 kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
254 routes->gsi[i] = -1;
255 }
256 }
257}
258
259
260
261
262
263
264
265
266
267
268static void kvm_flic_save(QEMUFile *f, void *opaque)
269{
270 KVMS390FLICState *flic = opaque;
271 int len = FLIC_SAVE_INITIAL_SIZE;
272 void *buf;
273 int count;
274
275 flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
276
277 buf = g_try_malloc0(len);
278 if (!buf) {
279
280
281
282 error_report("flic: couldn't allocate memory");
283 qemu_put_be64(f, FLIC_FAILED);
284 return;
285 }
286
287 count = __get_all_irqs(flic, &buf, len);
288 if (count < 0) {
289 error_report("flic: couldn't retrieve irqs from kernel, rc %d",
290 count);
291
292
293
294 qemu_put_be64(f, FLIC_FAILED);
295 } else {
296 qemu_put_be64(f, count);
297 qemu_put_buffer(f, (uint8_t *) buf,
298 count * sizeof(struct kvm_s390_irq));
299 }
300 g_free(buf);
301}
302
303
304
305
306
307
308
309
310
311
312
313static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
314{
315 uint64_t len = 0;
316 uint64_t count = 0;
317 void *buf = NULL;
318 int r = 0;
319
320 if (version_id != FLIC_SAVEVM_VERSION) {
321 r = -EINVAL;
322 goto out;
323 }
324
325 flic_enable_pfault((struct KVMS390FLICState *) opaque);
326
327 count = qemu_get_be64(f);
328 len = count * sizeof(struct kvm_s390_irq);
329 if (count == FLIC_FAILED) {
330 r = -EINVAL;
331 goto out;
332 }
333 if (count == 0) {
334 r = 0;
335 goto out;
336 }
337 buf = g_try_malloc0(len);
338 if (!buf) {
339 r = -ENOMEM;
340 goto out;
341 }
342
343 if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
344 r = -EINVAL;
345 goto out_free;
346 }
347 r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
348
349out_free:
350 g_free(buf);
351out:
352 return r;
353}
354
355static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
356{
357 KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
358 struct kvm_create_device cd = {0};
359 int ret;
360
361 flic_state->fd = -1;
362 if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
363 trace_flic_no_device_api(errno);
364 return;
365 }
366
367 cd.type = KVM_DEV_TYPE_FLIC;
368 ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
369 if (ret < 0) {
370 trace_flic_create_device(errno);
371 return;
372 }
373 flic_state->fd = cd.fd;
374
375
376 register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
377 kvm_flic_load, (void *) flic_state);
378}
379
380static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
381{
382 KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
383
384 unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
385}
386
387static void kvm_s390_flic_reset(DeviceState *dev)
388{
389 KVMS390FLICState *flic = KVM_S390_FLIC(dev);
390 struct kvm_device_attr attr = {
391 .group = KVM_DEV_FLIC_CLEAR_IRQS,
392 };
393 int rc = 0;
394
395 if (flic->fd == -1) {
396 return;
397 }
398
399 flic_disable_wait_pfault(flic);
400
401 rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
402 if (rc) {
403 trace_flic_reset_failed(errno);
404 }
405
406 flic_enable_pfault(flic);
407}
408
409static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
410{
411 DeviceClass *dc = DEVICE_CLASS(oc);
412 S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
413
414 dc->realize = kvm_s390_flic_realize;
415 dc->unrealize = kvm_s390_flic_unrealize;
416 dc->reset = kvm_s390_flic_reset;
417 fsc->register_io_adapter = kvm_s390_register_io_adapter;
418 fsc->io_adapter_map = kvm_s390_io_adapter_map;
419 fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
420 fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
421}
422
423static const TypeInfo kvm_s390_flic_info = {
424 .name = TYPE_KVM_S390_FLIC,
425 .parent = TYPE_S390_FLIC_COMMON,
426 .instance_size = sizeof(KVMS390FLICState),
427 .class_init = kvm_s390_flic_class_init,
428};
429
430static void kvm_s390_flic_register_types(void)
431{
432 type_register_static(&kvm_s390_flic_info);
433}
434
435type_init(kvm_s390_flic_register_types)
436