]> git.proxmox.com Git - mirror_qemu.git/blame - hw/misc/ivshmem.c
ivshmem: remove EventfdEntry.vector
[mirror_qemu.git] / hw / misc / ivshmem.c
CommitLineData
6cbf4c8c
CM
1/*
2 * Inter-VM Shared Memory PCI device.
3 *
4 * Author:
5 * Cam Macdonell <cam@cs.ualberta.ca>
6 *
7 * Based On: cirrus_vga.c
8 * Copyright (c) 2004 Fabrice Bellard
9 * Copyright (c) 2004 Makoto Suzuki (suzu)
10 *
11 * and rtl8139.c
12 * Copyright (c) 2006 Igor Kovalenko
13 *
14 * This code is licensed under the GNU GPL v2.
6b620ca3
PB
15 *
16 * Contributions after 2012-01-13 are licensed under the terms of the
17 * GNU GPL, version 2 or (at your option) any later version.
6cbf4c8c 18 */
83c9f4ca 19#include "hw/hw.h"
0d09e41a 20#include "hw/i386/pc.h"
83c9f4ca
PB
21#include "hw/pci/pci.h"
22#include "hw/pci/msix.h"
9c17d615 23#include "sysemu/kvm.h"
caf71f86 24#include "migration/migration.h"
d49b6836 25#include "qemu/error-report.h"
1de7afc9 26#include "qemu/event_notifier.h"
a2e9011b 27#include "qemu/fifo8.h"
dccfcd0e 28#include "sysemu/char.h"
d9453c93
MAL
29#include "sysemu/hostmem.h"
30#include "qapi/visitor.h"
6cbf4c8c 31
5105b1d8
DM
32#include "hw/misc/ivshmem.h"
33
6cbf4c8c
CM
34#include <sys/mman.h>
35#include <sys/types.h>
34bc07c5 36#include <limits.h>
6cbf4c8c 37
b8ef62a9
PB
38#define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET
39#define PCI_DEVICE_ID_IVSHMEM 0x1110
40
61ea2d86 41#define IVSHMEM_MAX_PEERS G_MAXUINT16
6cbf4c8c
CM
42#define IVSHMEM_IOEVENTFD 0
43#define IVSHMEM_MSI 1
44
45#define IVSHMEM_PEER 0
46#define IVSHMEM_MASTER 1
47
48#define IVSHMEM_REG_BAR_SIZE 0x100
49
50//#define DEBUG_IVSHMEM
51#ifdef DEBUG_IVSHMEM
52#define IVSHMEM_DPRINTF(fmt, ...) \
53 do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0)
54#else
55#define IVSHMEM_DPRINTF(fmt, ...)
56#endif
57
eb3fedf3
PC
58#define TYPE_IVSHMEM "ivshmem"
59#define IVSHMEM(obj) \
60 OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM)
61
d9453c93
MAL
62#define IVSHMEM_MEMDEV_PROP "memdev"
63
6cbf4c8c
CM
64typedef struct Peer {
65 int nb_eventfds;
563027cc 66 EventNotifier *eventfds;
6cbf4c8c
CM
67} Peer;
68
69typedef struct EventfdEntry {
70 PCIDevice *pdev;
6cbf4c8c
CM
71} EventfdEntry;
72
73typedef struct IVShmemState {
b7578eaa
AF
74 /*< private >*/
75 PCIDevice parent_obj;
76 /*< public >*/
77
d9453c93 78 HostMemoryBackend *hostmem;
6cbf4c8c
CM
79 uint32_t intrmask;
80 uint32_t intrstatus;
6cbf4c8c
CM
81
82 CharDriverState **eventfd_chr;
83 CharDriverState *server_chr;
a2e9011b 84 Fifo8 incoming_fifo;
cb06608e 85 MemoryRegion ivshmem_mmio;
6cbf4c8c 86
cb06608e
AK
87 /* We might need to register the BAR before we actually have the memory.
88 * So prepare a container MemoryRegion for the BAR immediately and
89 * add a subregion when we have the memory.
90 */
91 MemoryRegion bar;
92 MemoryRegion ivshmem;
6cbf4c8c 93 uint64_t ivshmem_size; /* size of shared memory region */
c08ba66f 94 uint32_t ivshmem_64bit;
6cbf4c8c
CM
95
96 Peer *peers;
f456179f 97 int nb_peers; /* how many peers we have space for */
6cbf4c8c
CM
98
99 int vm_id;
100 uint32_t vectors;
101 uint32_t features;
102 EventfdEntry *eventfd_table;
103
38e0735e
AL
104 Error *migration_blocker;
105
6cbf4c8c
CM
106 char * shmobj;
107 char * sizearg;
108 char * role;
109 int role_val; /* scalar to avoid multiple string comparisons */
110} IVShmemState;
111
112/* registers for the Inter-VM shared memory device */
113enum ivshmem_registers {
114 INTRMASK = 0,
115 INTRSTATUS = 4,
116 IVPOSITION = 8,
117 DOORBELL = 12,
118};
119
120static inline uint32_t ivshmem_has_feature(IVShmemState *ivs,
121 unsigned int feature) {
122 return (ivs->features & (1 << feature));
123}
124
6cbf4c8c 125/* accessing registers - based on rtl8139 */
d8a5da07 126static void ivshmem_update_irq(IVShmemState *s)
6cbf4c8c 127{
b7578eaa 128 PCIDevice *d = PCI_DEVICE(s);
6cbf4c8c
CM
129 int isr;
130 isr = (s->intrstatus & s->intrmask) & 0xffffffff;
131
132 /* don't print ISR resets */
133 if (isr) {
134 IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n",
dbc464d4 135 isr ? 1 : 0, s->intrstatus, s->intrmask);
6cbf4c8c
CM
136 }
137
9e64f8a3 138 pci_set_irq(d, (isr != 0));
6cbf4c8c
CM
139}
140
141static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
142{
143 IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
144
145 s->intrmask = val;
146
d8a5da07 147 ivshmem_update_irq(s);
6cbf4c8c
CM
148}
149
150static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
151{
152 uint32_t ret = s->intrmask;
153
154 IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret);
155
156 return ret;
157}
158
159static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
160{
161 IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val);
162
163 s->intrstatus = val;
164
d8a5da07 165 ivshmem_update_irq(s);
6cbf4c8c
CM
166}
167
168static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
169{
170 uint32_t ret = s->intrstatus;
171
172 /* reading ISR clears all interrupts */
173 s->intrstatus = 0;
174
d8a5da07 175 ivshmem_update_irq(s);
6cbf4c8c
CM
176
177 return ret;
178}
179
a8170e5e 180static void ivshmem_io_write(void *opaque, hwaddr addr,
cb06608e 181 uint64_t val, unsigned size)
6cbf4c8c
CM
182{
183 IVShmemState *s = opaque;
184
6cbf4c8c
CM
185 uint16_t dest = val >> 16;
186 uint16_t vector = val & 0xff;
187
188 addr &= 0xfc;
189
190 IVSHMEM_DPRINTF("writing to addr " TARGET_FMT_plx "\n", addr);
191 switch (addr)
192 {
193 case INTRMASK:
194 ivshmem_IntrMask_write(s, val);
195 break;
196
197 case INTRSTATUS:
198 ivshmem_IntrStatus_write(s, val);
199 break;
200
201 case DOORBELL:
202 /* check that dest VM ID is reasonable */
95c8425c 203 if (dest >= s->nb_peers) {
6cbf4c8c
CM
204 IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest);
205 break;
206 }
207
208 /* check doorbell range */
1b27d7a1 209 if (vector < s->peers[dest].nb_eventfds) {
563027cc
PB
210 IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector);
211 event_notifier_set(&s->peers[dest].eventfds[vector]);
f59bb378
MAL
212 } else {
213 IVSHMEM_DPRINTF("Invalid destination vector %d on VM %d\n",
214 vector, dest);
6cbf4c8c
CM
215 }
216 break;
217 default:
f59bb378 218 IVSHMEM_DPRINTF("Unhandled write " TARGET_FMT_plx "\n", addr);
6cbf4c8c
CM
219 }
220}
221
a8170e5e 222static uint64_t ivshmem_io_read(void *opaque, hwaddr addr,
cb06608e 223 unsigned size)
6cbf4c8c
CM
224{
225
226 IVShmemState *s = opaque;
227 uint32_t ret;
228
229 switch (addr)
230 {
231 case INTRMASK:
232 ret = ivshmem_IntrMask_read(s);
233 break;
234
235 case INTRSTATUS:
236 ret = ivshmem_IntrStatus_read(s);
237 break;
238
239 case IVPOSITION:
240 /* return my VM ID if the memory is mapped */
f689d281 241 if (memory_region_is_mapped(&s->ivshmem)) {
6cbf4c8c
CM
242 ret = s->vm_id;
243 } else {
244 ret = -1;
245 }
246 break;
247
248 default:
249 IVSHMEM_DPRINTF("why are we reading " TARGET_FMT_plx "\n", addr);
250 ret = 0;
251 }
252
253 return ret;
254}
255
cb06608e
AK
256static const MemoryRegionOps ivshmem_mmio_ops = {
257 .read = ivshmem_io_read,
258 .write = ivshmem_io_write,
259 .endianness = DEVICE_NATIVE_ENDIAN,
260 .impl = {
261 .min_access_size = 4,
262 .max_access_size = 4,
263 },
6cbf4c8c
CM
264};
265
266static void ivshmem_receive(void *opaque, const uint8_t *buf, int size)
267{
268 IVShmemState *s = opaque;
269
f59bb378 270 IVSHMEM_DPRINTF("ivshmem_receive 0x%02x size: %d\n", *buf, size);
6cbf4c8c 271
f59bb378 272 ivshmem_IntrStatus_write(s, *buf);
6cbf4c8c
CM
273}
274
275static int ivshmem_can_receive(void * opaque)
276{
b8ab854b 277 return sizeof(long);
6cbf4c8c
CM
278}
279
280static void ivshmem_event(void *opaque, int event)
281{
282 IVSHMEM_DPRINTF("ivshmem_event %d\n", event);
283}
284
285static void fake_irqfd(void *opaque, const uint8_t *buf, int size) {
286
287 EventfdEntry *entry = opaque;
288 PCIDevice *pdev = entry->pdev;
d160f3f7
MAL
289 IVShmemState *s = IVSHMEM(pdev);
290 int vector = entry - s->eventfd_table;
6cbf4c8c 291
d160f3f7
MAL
292 IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector);
293 msix_notify(pdev, vector);
6cbf4c8c
CM
294}
295
563027cc
PB
296static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *n,
297 int vector)
6cbf4c8c
CM
298{
299 /* create a event character device based on the passed eventfd */
300 IVShmemState *s = opaque;
301 CharDriverState * chr;
563027cc 302 int eventfd = event_notifier_get_fd(n);
6cbf4c8c
CM
303
304 chr = qemu_chr_open_eventfd(eventfd);
305
306 if (chr == NULL) {
36617792 307 error_report("creating chardriver for eventfd %d failed", eventfd);
03977ad5 308 return NULL;
6cbf4c8c 309 }
456d6069 310 qemu_chr_fe_claim_no_fail(chr);
6cbf4c8c
CM
311
312 /* if MSI is supported we need multiple interrupts */
313 if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
b7578eaa 314 s->eventfd_table[vector].pdev = PCI_DEVICE(s);
6cbf4c8c
CM
315
316 qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd,
317 ivshmem_event, &s->eventfd_table[vector]);
318 } else {
319 qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive,
320 ivshmem_event, s);
321 }
322
323 return chr;
324
325}
326
d58d7e84
MAL
327static int check_shm_size(IVShmemState *s, int fd, Error **errp)
328{
6cbf4c8c
CM
329 /* check that the guest isn't going to try and map more memory than the
330 * the object has allocated return -1 to indicate error */
331
332 struct stat buf;
333
5edbdbcd 334 if (fstat(fd, &buf) < 0) {
d58d7e84
MAL
335 error_setg(errp, "exiting: fstat on fd %d failed: %s",
336 fd, strerror(errno));
5edbdbcd
HZ
337 return -1;
338 }
6cbf4c8c
CM
339
340 if (s->ivshmem_size > buf.st_size) {
d58d7e84
MAL
341 error_setg(errp, "Requested memory size greater"
342 " than shared object size (%" PRIu64 " > %" PRIu64")",
343 s->ivshmem_size, (uint64_t)buf.st_size);
6cbf4c8c
CM
344 return -1;
345 } else {
346 return 0;
347 }
348}
349
350/* create the shared memory BAR when we are not using the server, so we can
351 * create the BAR and map the memory immediately */
d58d7e84
MAL
352static int create_shared_memory_BAR(IVShmemState *s, int fd, uint8_t attr,
353 Error **errp)
354{
6cbf4c8c
CM
355 void * ptr;
356
6cbf4c8c 357 ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
d58d7e84
MAL
358 if (ptr == MAP_FAILED) {
359 error_setg_errno(errp, errno, "Failed to mmap shared memory");
360 return -1;
361 }
362
3c161542 363 memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), "ivshmem.bar2",
cb06608e 364 s->ivshmem_size, ptr);
eb3fedf3 365 vmstate_register_ram(&s->ivshmem, DEVICE(s));
cb06608e 366 memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
6cbf4c8c
CM
367
368 /* region for shared memory */
9113e3f3 369 pci_register_bar(PCI_DEVICE(s), 2, attr, &s->bar);
d58d7e84
MAL
370
371 return 0;
6cbf4c8c
CM
372}
373
563027cc
PB
374static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i)
375{
376 memory_region_add_eventfd(&s->ivshmem_mmio,
377 DOORBELL,
378 4,
379 true,
380 (posn << 16) | i,
753d5e14 381 &s->peers[posn].eventfds[i]);
563027cc
PB
382}
383
384static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i)
385{
386 memory_region_del_eventfd(&s->ivshmem_mmio,
387 DOORBELL,
388 4,
389 true,
390 (posn << 16) | i,
753d5e14 391 &s->peers[posn].eventfds[i]);
563027cc
PB
392}
393
f456179f 394static void close_peer_eventfds(IVShmemState *s, int posn)
6cbf4c8c 395{
f456179f 396 int i, n;
6cbf4c8c 397
98609cd8
PB
398 if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
399 return;
400 }
363ba1c7 401 if (posn < 0 || posn >= s->nb_peers) {
ffa99afd 402 error_report("invalid peer %d", posn);
363ba1c7
SH
403 return;
404 }
98609cd8 405
f456179f 406 n = s->peers[posn].nb_eventfds;
6cbf4c8c 407
b6a1f3a5 408 memory_region_transaction_begin();
f456179f 409 for (i = 0; i < n; i++) {
563027cc 410 ivshmem_del_eventfd(s, posn, i);
b6a1f3a5
PB
411 }
412 memory_region_transaction_commit();
f456179f 413 for (i = 0; i < n; i++) {
563027cc 414 event_notifier_cleanup(&s->peers[posn].eventfds[i]);
6cbf4c8c
CM
415 }
416
7267c094 417 g_free(s->peers[posn].eventfds);
6cbf4c8c
CM
418 s->peers[posn].nb_eventfds = 0;
419}
420
6cbf4c8c 421/* this function increase the dynamic storage need to store data about other
f456179f 422 * peers */
1300b273 423static int resize_peers(IVShmemState *s, int new_min_size)
34bc07c5 424{
6cbf4c8c 425
1300b273 426 int j, old_size;
6cbf4c8c 427
61ea2d86
MAL
428 /* limit number of max peers */
429 if (new_min_size <= 0 || new_min_size > IVSHMEM_MAX_PEERS) {
34bc07c5
SK
430 return -1;
431 }
1300b273 432 if (new_min_size <= s->nb_peers) {
34bc07c5
SK
433 return 0;
434 }
6cbf4c8c 435
1300b273
MAL
436 old_size = s->nb_peers;
437 s->nb_peers = new_min_size;
438
f456179f 439 IVSHMEM_DPRINTF("bumping storage to %d peers\n", s->nb_peers);
1300b273 440
7267c094 441 s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer));
6cbf4c8c 442
1300b273 443 for (j = old_size; j < s->nb_peers; j++) {
81e507f0 444 s->peers[j].eventfds = g_new0(EventNotifier, s->vectors);
6cbf4c8c
CM
445 s->peers[j].nb_eventfds = 0;
446 }
34bc07c5
SK
447
448 return 0;
6cbf4c8c
CM
449}
450
0f14fd71
MAL
451static bool fifo_update_and_get(IVShmemState *s, const uint8_t *buf, int size,
452 void *data, size_t len)
453{
454 const uint8_t *p;
455 uint32_t num;
456
457 assert(len <= sizeof(long)); /* limitation of the fifo */
458 if (fifo8_is_empty(&s->incoming_fifo) && size == len) {
459 memcpy(data, buf, size);
460 return true;
461 }
462
463 IVSHMEM_DPRINTF("short read of %d bytes\n", size);
464
465 num = MIN(size, sizeof(long) - fifo8_num_used(&s->incoming_fifo));
466 fifo8_push_all(&s->incoming_fifo, buf, num);
467
468 if (fifo8_num_used(&s->incoming_fifo) < len) {
469 assert(num == 0);
470 return false;
471 }
472
473 size -= num;
474 buf += num;
475 p = fifo8_pop_buf(&s->incoming_fifo, len, &num);
476 assert(num == len);
477
478 memcpy(data, p, len);
479
480 if (size > 0) {
481 fifo8_push_all(&s->incoming_fifo, buf, size);
482 }
483
484 return true;
485}
486
a2e9011b 487static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
6cbf4c8c
CM
488{
489 IVShmemState *s = opaque;
dee2151e 490 int incoming_fd;
9a2f0e64 491 int new_eventfd;
6cbf4c8c 492 long incoming_posn;
d58d7e84 493 Error *err = NULL;
9a2f0e64 494 Peer *peer;
6cbf4c8c 495
0f14fd71
MAL
496 if (!fifo_update_and_get(s, buf, size,
497 &incoming_posn, sizeof(incoming_posn))) {
498 return;
a2e9011b
SH
499 }
500
363ba1c7
SH
501 if (incoming_posn < -1) {
502 IVSHMEM_DPRINTF("invalid incoming_posn %ld\n", incoming_posn);
503 return;
504 }
505
6cbf4c8c 506 /* pick off s->server_chr->msgfd and store it, posn should accompany msg */
dee2151e
MAL
507 incoming_fd = qemu_chr_fe_get_msgfd(s->server_chr);
508 IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, incoming_fd);
6cbf4c8c 509
f456179f 510 /* make sure we have enough space for this peer */
6cbf4c8c 511 if (incoming_posn >= s->nb_peers) {
1300b273
MAL
512 if (resize_peers(s, incoming_posn + 1) < 0) {
513 error_report("failed to resize peers array");
dee2151e
MAL
514 if (incoming_fd != -1) {
515 close(incoming_fd);
34bc07c5
SK
516 }
517 return;
518 }
6cbf4c8c
CM
519 }
520
9a2f0e64
MAL
521 peer = &s->peers[incoming_posn];
522
dee2151e 523 if (incoming_fd == -1) {
6cbf4c8c 524 /* if posn is positive and unseen before then this is our posn*/
81e507f0 525 if (incoming_posn >= 0 && s->vm_id == -1) {
6cbf4c8c
CM
526 /* receive our posn */
527 s->vm_id = incoming_posn;
6cbf4c8c 528 } else {
f456179f 529 /* otherwise an fd == -1 means an existing peer has gone away */
6cbf4c8c 530 IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn);
f456179f 531 close_peer_eventfds(s, incoming_posn);
6cbf4c8c 532 }
6f8a16d5 533 return;
6cbf4c8c
CM
534 }
535
6cbf4c8c
CM
536 /* if the position is -1, then it's shared memory region fd */
537 if (incoming_posn == -1) {
6cbf4c8c
CM
538 void * map_ptr;
539
f689d281 540 if (memory_region_is_mapped(&s->ivshmem)) {
945001a1
MAL
541 error_report("shm already initialized");
542 close(incoming_fd);
543 return;
544 }
545
d58d7e84
MAL
546 if (check_shm_size(s, incoming_fd, &err) == -1) {
547 error_report_err(err);
548 close(incoming_fd);
549 return;
6cbf4c8c
CM
550 }
551
552 /* mmap the region and map into the BAR2 */
553 map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED,
554 incoming_fd, 0);
d58d7e84
MAL
555 if (map_ptr == MAP_FAILED) {
556 error_report("Failed to mmap shared memory %s", strerror(errno));
557 close(incoming_fd);
558 return;
559 }
3c161542 560 memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s),
cb06608e 561 "ivshmem.bar2", s->ivshmem_size, map_ptr);
eb3fedf3 562 vmstate_register_ram(&s->ivshmem, DEVICE(s));
6cbf4c8c 563
7f9efb6b 564 IVSHMEM_DPRINTF("guest h/w addr = %p, size = %" PRIu64 "\n",
dbc464d4 565 map_ptr, s->ivshmem_size);
6cbf4c8c 566
cb06608e 567 memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
6cbf4c8c 568
f689d281 569 close(incoming_fd);
6cbf4c8c
CM
570 return;
571 }
572
9a2f0e64
MAL
573 /* each peer has an associated array of eventfds, and we keep
574 * track of how many eventfds received so far */
575 /* get a new eventfd: */
1ee57de4
MAL
576 if (peer->nb_eventfds >= s->vectors) {
577 error_report("Too many eventfd received, device has %d vectors",
578 s->vectors);
579 close(incoming_fd);
580 return;
581 }
582
9a2f0e64 583 new_eventfd = peer->nb_eventfds++;
6cbf4c8c 584
f456179f 585 /* this is an eventfd for a particular peer VM */
6cbf4c8c 586 IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn,
9a2f0e64
MAL
587 new_eventfd, incoming_fd);
588 event_notifier_init_fd(&peer->eventfds[new_eventfd], incoming_fd);
6cbf4c8c 589
6cbf4c8c 590 if (incoming_posn == s->vm_id) {
9a2f0e64
MAL
591 s->eventfd_chr[new_eventfd] = create_eventfd_chr_device(s,
592 &s->peers[s->vm_id].eventfds[new_eventfd],
593 new_eventfd);
6cbf4c8c
CM
594 }
595
596 if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
9a2f0e64 597 ivshmem_add_eventfd(s, incoming_posn, new_eventfd);
6cbf4c8c 598 }
6cbf4c8c
CM
599}
600
5105b1d8
DM
601static void ivshmem_check_version(void *opaque, const uint8_t * buf, int size)
602{
603 IVShmemState *s = opaque;
604 int tmp;
605 long version;
606
607 if (!fifo_update_and_get(s, buf, size,
608 &version, sizeof(version))) {
609 return;
610 }
611
612 tmp = qemu_chr_fe_get_msgfd(s->server_chr);
613 if (tmp != -1 || version != IVSHMEM_PROTOCOL_VERSION) {
614 fprintf(stderr, "incompatible version, you are connecting to a ivshmem-"
615 "server using a different protocol please check your setup\n");
616 qemu_chr_delete(s->server_chr);
617 s->server_chr = NULL;
618 return;
619 }
620
621 IVSHMEM_DPRINTF("version check ok, switch to real chardev handler\n");
622 qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read,
623 ivshmem_event, s);
624}
625
4490c711
MT
626/* Select the MSI-X vectors used by device.
627 * ivshmem maps events to vectors statically, so
628 * we just enable all vectors on init and after reset. */
629static void ivshmem_use_msix(IVShmemState * s)
630{
b7578eaa 631 PCIDevice *d = PCI_DEVICE(s);
4490c711
MT
632 int i;
633
f59bb378 634 IVSHMEM_DPRINTF("%s, msix present: %d\n", __func__, msix_present(d));
b7578eaa 635 if (!msix_present(d)) {
4490c711
MT
636 return;
637 }
638
639 for (i = 0; i < s->vectors; i++) {
b7578eaa 640 msix_vector_use(d, i);
4490c711
MT
641 }
642}
643
6cbf4c8c
CM
644static void ivshmem_reset(DeviceState *d)
645{
eb3fedf3 646 IVShmemState *s = IVSHMEM(d);
6cbf4c8c
CM
647
648 s->intrstatus = 0;
972ad215 649 s->intrmask = 0;
4490c711 650 ivshmem_use_msix(s);
6cbf4c8c
CM
651}
652
d58d7e84 653static int ivshmem_setup_msi(IVShmemState * s)
4490c711 654{
b7578eaa 655 if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) {
d58d7e84 656 return -1;
6cbf4c8c
CM
657 }
658
1116b539
AW
659 IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors);
660
5cbdb3a3 661 /* allocate QEMU char devices for receiving interrupts */
7267c094 662 s->eventfd_table = g_malloc0(s->vectors * sizeof(EventfdEntry));
4490c711
MT
663
664 ivshmem_use_msix(s);
d58d7e84 665 return 0;
6cbf4c8c
CM
666}
667
4490c711 668static void ivshmem_write_config(PCIDevice *pci_dev, uint32_t address,
d58d7e84 669 uint32_t val, int len)
4490c711
MT
670{
671 pci_default_write_config(pci_dev, address, val, len);
4490c711
MT
672}
673
d58d7e84 674static void pci_ivshmem_realize(PCIDevice *dev, Error **errp)
6cbf4c8c 675{
eb3fedf3 676 IVShmemState *s = IVSHMEM(dev);
6cbf4c8c 677 uint8_t *pci_conf;
9113e3f3
MAL
678 uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
679 PCI_BASE_ADDRESS_MEM_PREFETCH;
6cbf4c8c 680
d9453c93
MAL
681 if (!!s->server_chr + !!s->shmobj + !!s->hostmem != 1) {
682 error_setg(errp, "You must specify either a shmobj, a chardev"
683 " or a hostmem");
684 return;
685 }
686
687 if (s->hostmem) {
688 MemoryRegion *mr;
689
690 if (s->sizearg) {
691 g_warning("size argument ignored with hostmem");
692 }
693
694 mr = host_memory_backend_get_memory(s->hostmem, errp);
695 s->ivshmem_size = memory_region_size(mr);
696 } else if (s->sizearg == NULL) {
6cbf4c8c 697 s->ivshmem_size = 4 << 20; /* 4 MB default */
d58d7e84 698 } else {
2c04752c
MAL
699 char *end;
700 int64_t size = qemu_strtosz(s->sizearg, &end);
701 if (size < 0 || *end != '\0' || !is_power_of_2(size)) {
702 error_setg(errp, "Invalid size %s", s->sizearg);
d58d7e84
MAL
703 return;
704 }
2c04752c 705 s->ivshmem_size = size;
6cbf4c8c
CM
706 }
707
a2e9011b 708 fifo8_create(&s->incoming_fifo, sizeof(long));
1f8552df 709
6cbf4c8c
CM
710 /* IRQFD requires MSI */
711 if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
712 !ivshmem_has_feature(s, IVSHMEM_MSI)) {
d58d7e84
MAL
713 error_setg(errp, "ioeventfd/irqfd requires MSI");
714 return;
6cbf4c8c
CM
715 }
716
717 /* check that role is reasonable */
718 if (s->role) {
719 if (strncmp(s->role, "peer", 5) == 0) {
720 s->role_val = IVSHMEM_PEER;
721 } else if (strncmp(s->role, "master", 7) == 0) {
722 s->role_val = IVSHMEM_MASTER;
723 } else {
d58d7e84
MAL
724 error_setg(errp, "'role' must be 'peer' or 'master'");
725 return;
6cbf4c8c
CM
726 }
727 } else {
728 s->role_val = IVSHMEM_MASTER; /* default */
729 }
730
731 if (s->role_val == IVSHMEM_PEER) {
f231b88d
CR
732 error_setg(&s->migration_blocker,
733 "Migration is disabled when using feature 'peer mode' in device 'ivshmem'");
38e0735e 734 migrate_add_blocker(s->migration_blocker);
6cbf4c8c
CM
735 }
736
b7578eaa 737 pci_conf = dev->config;
6cbf4c8c 738 pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
6cbf4c8c
CM
739
740 pci_config_set_interrupt_pin(pci_conf, 1);
741
3c161542 742 memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s,
cb06608e
AK
743 "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE);
744
6cbf4c8c 745 /* region for registers*/
b7578eaa 746 pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
e824b2cc 747 &s->ivshmem_mmio);
cb06608e 748
3c161542 749 memory_region_init(&s->bar, OBJECT(s), "ivshmem-bar2-container", s->ivshmem_size);
c08ba66f 750 if (s->ivshmem_64bit) {
9113e3f3 751 attr |= PCI_BASE_ADDRESS_MEM_TYPE_64;
c08ba66f 752 }
6cbf4c8c 753
d9453c93
MAL
754 if (s->hostmem != NULL) {
755 MemoryRegion *mr;
756
757 IVSHMEM_DPRINTF("using hostmem\n");
758
759 mr = host_memory_backend_get_memory(MEMORY_BACKEND(s->hostmem), errp);
760 vmstate_register_ram(mr, DEVICE(s));
761 memory_region_add_subregion(&s->bar, 0, mr);
762 pci_register_bar(PCI_DEVICE(s), 2, attr, &s->bar);
763 } else if (s->server_chr != NULL) {
36617792
MAL
764 if (strncmp(s->server_chr->filename, "unix:", 5)) {
765 error_setg(errp, "chardev is not a unix client socket");
766 return;
767 }
768
6cbf4c8c
CM
769 /* if we get a UNIX socket as the parameter we will talk
770 * to the ivshmem server to receive the memory region */
771
6cbf4c8c 772 IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
dbc464d4 773 s->server_chr->filename);
6cbf4c8c 774
d58d7e84
MAL
775 if (ivshmem_has_feature(s, IVSHMEM_MSI) &&
776 ivshmem_setup_msi(s)) {
777 error_setg(errp, "msix initialization failed");
778 return;
6cbf4c8c
CM
779 }
780
f456179f 781 /* we allocate enough space for 16 peers and grow as needed */
1300b273 782 resize_peers(s, 16);
6cbf4c8c
CM
783 s->vm_id = -1;
784
9113e3f3 785 pci_register_bar(dev, 2, attr, &s->bar);
6cbf4c8c 786
7267c094 787 s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *));
6cbf4c8c 788
5105b1d8
DM
789 qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive,
790 ivshmem_check_version, ivshmem_event, s);
6cbf4c8c
CM
791 } else {
792 /* just map the file immediately, we're not using a server */
793 int fd;
794
6cbf4c8c
CM
795 IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj);
796
797 /* try opening with O_EXCL and if it succeeds zero the memory
798 * by truncating to 0 */
799 if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL,
800 S_IRWXU|S_IRWXG|S_IRWXO)) > 0) {
801 /* truncate file to length PCI device's memory */
802 if (ftruncate(fd, s->ivshmem_size) != 0) {
dbc464d4 803 error_report("could not truncate shared file");
6cbf4c8c
CM
804 }
805
806 } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR,
807 S_IRWXU|S_IRWXG|S_IRWXO)) < 0) {
d58d7e84
MAL
808 error_setg(errp, "could not open shared file");
809 return;
6cbf4c8c
CM
810 }
811
d58d7e84
MAL
812 if (check_shm_size(s, fd, errp) == -1) {
813 return;
6cbf4c8c
CM
814 }
815
d58d7e84 816 create_shared_memory_BAR(s, fd, attr, errp);
f689d281 817 close(fd);
6cbf4c8c 818 }
6cbf4c8c
CM
819}
820
d58d7e84 821static void pci_ivshmem_exit(PCIDevice *dev)
6cbf4c8c 822{
eb3fedf3 823 IVShmemState *s = IVSHMEM(dev);
f64a078d
MAL
824 int i;
825
826 fifo8_destroy(&s->incoming_fifo);
6cbf4c8c 827
38e0735e
AL
828 if (s->migration_blocker) {
829 migrate_del_blocker(s->migration_blocker);
830 error_free(s->migration_blocker);
831 }
832
f689d281 833 if (memory_region_is_mapped(&s->ivshmem)) {
d9453c93
MAL
834 if (!s->hostmem) {
835 void *addr = memory_region_get_ram_ptr(&s->ivshmem);
836
837 if (munmap(addr, s->ivshmem_size) == -1) {
838 error_report("Failed to munmap shared memory %s",
839 strerror(errno));
840 }
841 }
f64a078d
MAL
842
843 vmstate_unregister_ram(&s->ivshmem, DEVICE(dev));
844 memory_region_del_subregion(&s->bar, &s->ivshmem);
f64a078d
MAL
845 }
846
847 if (s->eventfd_chr) {
848 for (i = 0; i < s->vectors; i++) {
849 if (s->eventfd_chr[i]) {
850 qemu_chr_free(s->eventfd_chr[i]);
851 }
852 }
853 g_free(s->eventfd_chr);
854 }
855
856 if (s->peers) {
857 for (i = 0; i < s->nb_peers; i++) {
f456179f 858 close_peer_eventfds(s, i);
f64a078d
MAL
859 }
860 g_free(s->peers);
861 }
862
863 if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
864 msix_uninit_exclusive_bar(dev);
865 }
866
867 g_free(s->eventfd_table);
6cbf4c8c
CM
868}
869
1f8552df
MAL
870static bool test_msix(void *opaque, int version_id)
871{
872 IVShmemState *s = opaque;
873
874 return ivshmem_has_feature(s, IVSHMEM_MSI);
875}
876
877static bool test_no_msix(void *opaque, int version_id)
878{
879 return !test_msix(opaque, version_id);
880}
881
882static int ivshmem_pre_load(void *opaque)
883{
884 IVShmemState *s = opaque;
885
886 if (s->role_val == IVSHMEM_PEER) {
887 error_report("'peer' devices are not migratable");
888 return -EINVAL;
889 }
890
891 return 0;
892}
893
894static int ivshmem_post_load(void *opaque, int version_id)
895{
896 IVShmemState *s = opaque;
897
898 if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
899 ivshmem_use_msix(s);
900 }
901
902 return 0;
903}
904
905static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id)
906{
907 IVShmemState *s = opaque;
908 PCIDevice *pdev = PCI_DEVICE(s);
909 int ret;
910
911 IVSHMEM_DPRINTF("ivshmem_load_old\n");
912
913 if (version_id != 0) {
914 return -EINVAL;
915 }
916
917 if (s->role_val == IVSHMEM_PEER) {
918 error_report("'peer' devices are not migratable");
919 return -EINVAL;
920 }
921
922 ret = pci_device_load(pdev, f);
923 if (ret) {
924 return ret;
925 }
926
927 if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
928 msix_load(pdev, f);
929 ivshmem_use_msix(s);
930 } else {
931 s->intrstatus = qemu_get_be32(f);
932 s->intrmask = qemu_get_be32(f);
933 }
934
935 return 0;
936}
937
938static const VMStateDescription ivshmem_vmsd = {
939 .name = "ivshmem",
940 .version_id = 1,
941 .minimum_version_id = 1,
942 .pre_load = ivshmem_pre_load,
943 .post_load = ivshmem_post_load,
944 .fields = (VMStateField[]) {
945 VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
946
947 VMSTATE_MSIX_TEST(parent_obj, IVShmemState, test_msix),
948 VMSTATE_UINT32_TEST(intrstatus, IVShmemState, test_no_msix),
949 VMSTATE_UINT32_TEST(intrmask, IVShmemState, test_no_msix),
950
951 VMSTATE_END_OF_LIST()
952 },
953 .load_state_old = ivshmem_load_old,
954 .minimum_version_id_old = 0
955};
956
40021f08
AL
957static Property ivshmem_properties[] = {
958 DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
959 DEFINE_PROP_STRING("size", IVShmemState, sizearg),
960 DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
961 DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false),
962 DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true),
963 DEFINE_PROP_STRING("shm", IVShmemState, shmobj),
964 DEFINE_PROP_STRING("role", IVShmemState, role),
c08ba66f 965 DEFINE_PROP_UINT32("use64", IVShmemState, ivshmem_64bit, 1),
40021f08
AL
966 DEFINE_PROP_END_OF_LIST(),
967};
968
969static void ivshmem_class_init(ObjectClass *klass, void *data)
970{
39bffca2 971 DeviceClass *dc = DEVICE_CLASS(klass);
40021f08
AL
972 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
973
d58d7e84
MAL
974 k->realize = pci_ivshmem_realize;
975 k->exit = pci_ivshmem_exit;
976 k->config_write = ivshmem_write_config;
b8ef62a9
PB
977 k->vendor_id = PCI_VENDOR_ID_IVSHMEM;
978 k->device_id = PCI_DEVICE_ID_IVSHMEM;
40021f08 979 k->class_id = PCI_CLASS_MEMORY_RAM;
39bffca2
AL
980 dc->reset = ivshmem_reset;
981 dc->props = ivshmem_properties;
1f8552df 982 dc->vmsd = &ivshmem_vmsd;
125ee0ed 983 set_bit(DEVICE_CATEGORY_MISC, dc->categories);
d383537d 984 dc->desc = "Inter-VM shared memory";
40021f08
AL
985}
986
d9453c93
MAL
987static void ivshmem_check_memdev_is_busy(Object *obj, const char *name,
988 Object *val, Error **errp)
989{
990 MemoryRegion *mr;
991
992 mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), errp);
993 if (memory_region_is_mapped(mr)) {
994 char *path = object_get_canonical_path_component(val);
995 error_setg(errp, "can't use already busy memdev: %s", path);
996 g_free(path);
997 } else {
998 qdev_prop_allow_set_link_before_realize(obj, name, val, errp);
999 }
1000}
1001
1002static void ivshmem_init(Object *obj)
1003{
1004 IVShmemState *s = IVSHMEM(obj);
1005
1006 object_property_add_link(obj, IVSHMEM_MEMDEV_PROP, TYPE_MEMORY_BACKEND,
1007 (Object **)&s->hostmem,
1008 ivshmem_check_memdev_is_busy,
1009 OBJ_PROP_LINK_UNREF_ON_RELEASE,
1010 &error_abort);
1011}
1012
8c43a6f0 1013static const TypeInfo ivshmem_info = {
eb3fedf3 1014 .name = TYPE_IVSHMEM,
39bffca2
AL
1015 .parent = TYPE_PCI_DEVICE,
1016 .instance_size = sizeof(IVShmemState),
d9453c93 1017 .instance_init = ivshmem_init,
39bffca2 1018 .class_init = ivshmem_class_init,
6cbf4c8c
CM
1019};
1020
83f7d43a 1021static void ivshmem_register_types(void)
6cbf4c8c 1022{
39bffca2 1023 type_register_static(&ivshmem_info);
6cbf4c8c
CM
1024}
1025
83f7d43a 1026type_init(ivshmem_register_types)