blob: 83d7bd3e5f0d38d714ce2393b32eaca0a7da09f4 [file] [log] [blame]
Cam Macdonell6cbf4c82010-07-27 10:54:13 -06001/*
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.
Paolo Bonzini6b620ca2012-01-13 17:44:23 +010015 *
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.
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060018 */
Paolo Bonzini83c9f4c2013-02-04 15:40:22 +010019#include "hw/hw.h"
Paolo Bonzini0d09e412013-02-05 17:06:20 +010020#include "hw/i386/pc.h"
Paolo Bonzini83c9f4c2013-02-04 15:40:22 +010021#include "hw/pci/pci.h"
Marc-André Lureau660c97e2015-07-09 15:50:13 +020022#include "hw/pci/msi.h"
Paolo Bonzini83c9f4c2013-02-04 15:40:22 +010023#include "hw/pci/msix.h"
Paolo Bonzini9c17d612012-12-17 18:20:04 +010024#include "sysemu/kvm.h"
Paolo Bonzinicaf71f82012-12-17 18:19:50 +010025#include "migration/migration.h"
Markus Armbrusterd49b6832015-03-17 18:29:20 +010026#include "qemu/error-report.h"
Paolo Bonzini1de7afc2012-12-17 18:20:00 +010027#include "qemu/event_notifier.h"
Stefan Hajnoczia2e90112014-09-15 18:40:05 +020028#include "qemu/fifo8.h"
Paolo Bonzinidccfcd02013-04-08 16:55:25 +020029#include "sysemu/char.h"
Marc-André Lureaud9453c92015-06-30 00:10:16 +020030#include "sysemu/hostmem.h"
31#include "qapi/visitor.h"
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060032
David Marchand5105b1d2015-06-16 17:43:34 +020033#include "hw/misc/ivshmem.h"
34
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060035#include <sys/mman.h>
36#include <sys/types.h>
Sebastian Krahmer34bc07c2014-09-15 18:40:07 +020037#include <limits.h>
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060038
Paolo Bonzinib8ef62a2012-12-13 10:19:37 +010039#define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET
40#define PCI_DEVICE_ID_IVSHMEM 0x1110
41
Marc-André Lureau61ea2d82015-09-15 16:55:10 +020042#define IVSHMEM_MAX_PEERS G_MAXUINT16
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060043#define IVSHMEM_IOEVENTFD 0
44#define IVSHMEM_MSI 1
45
46#define IVSHMEM_PEER 0
47#define IVSHMEM_MASTER 1
48
49#define IVSHMEM_REG_BAR_SIZE 0x100
50
51//#define DEBUG_IVSHMEM
52#ifdef DEBUG_IVSHMEM
53#define IVSHMEM_DPRINTF(fmt, ...) \
54 do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0)
55#else
56#define IVSHMEM_DPRINTF(fmt, ...)
57#endif
58
Peter Crosthwaiteeb3fedf2013-06-24 16:59:29 +100059#define TYPE_IVSHMEM "ivshmem"
60#define IVSHMEM(obj) \
61 OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM)
62
Marc-André Lureaud9453c92015-06-30 00:10:16 +020063#define IVSHMEM_MEMDEV_PROP "memdev"
64
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060065typedef struct Peer {
66 int nb_eventfds;
Paolo Bonzini563027c2012-07-05 17:16:25 +020067 EventNotifier *eventfds;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060068} Peer;
69
Marc-André Lureau0f573502015-07-27 12:59:19 +020070typedef struct MSIVector {
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060071 PCIDevice *pdev;
Marc-André Lureau660c97e2015-07-09 15:50:13 +020072 int virq;
Marc-André Lureau0f573502015-07-27 12:59:19 +020073} MSIVector;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060074
75typedef struct IVShmemState {
Andreas Färberb7578ea2013-06-30 15:15:15 +020076 /*< private >*/
77 PCIDevice parent_obj;
78 /*< public >*/
79
Marc-André Lureaud9453c92015-06-30 00:10:16 +020080 HostMemoryBackend *hostmem;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060081 uint32_t intrmask;
82 uint32_t intrstatus;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060083
84 CharDriverState **eventfd_chr;
85 CharDriverState *server_chr;
Stefan Hajnoczia2e90112014-09-15 18:40:05 +020086 Fifo8 incoming_fifo;
Avi Kivitycb066082011-08-08 16:09:12 +030087 MemoryRegion ivshmem_mmio;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060088
Avi Kivitycb066082011-08-08 16:09:12 +030089 /* We might need to register the BAR before we actually have the memory.
90 * So prepare a container MemoryRegion for the BAR immediately and
91 * add a subregion when we have the memory.
92 */
93 MemoryRegion bar;
94 MemoryRegion ivshmem;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060095 uint64_t ivshmem_size; /* size of shared memory region */
Gerd Hoffmannc08ba662012-09-13 11:08:02 +020096 uint32_t ivshmem_64bit;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -060097
98 Peer *peers;
Marc-André Lureauf4561792015-06-23 13:38:46 +020099 int nb_peers; /* how many peers we have space for */
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600100
101 int vm_id;
102 uint32_t vectors;
103 uint32_t features;
Marc-André Lureau0f573502015-07-27 12:59:19 +0200104 MSIVector *msi_vectors;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600105
Anthony Liguori38e07352011-11-14 15:09:44 -0600106 Error *migration_blocker;
107
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600108 char * shmobj;
109 char * sizearg;
110 char * role;
111 int role_val; /* scalar to avoid multiple string comparisons */
112} IVShmemState;
113
114/* registers for the Inter-VM shared memory device */
115enum ivshmem_registers {
116 INTRMASK = 0,
117 INTRSTATUS = 4,
118 IVPOSITION = 8,
119 DOORBELL = 12,
120};
121
122static inline uint32_t ivshmem_has_feature(IVShmemState *ivs,
123 unsigned int feature) {
124 return (ivs->features & (1 << feature));
125}
126
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600127/* accessing registers - based on rtl8139 */
Marc-André Lureaud8a5da02015-06-18 15:00:52 +0200128static void ivshmem_update_irq(IVShmemState *s)
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600129{
Andreas Färberb7578ea2013-06-30 15:15:15 +0200130 PCIDevice *d = PCI_DEVICE(s);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600131 int isr;
132 isr = (s->intrstatus & s->intrmask) & 0xffffffff;
133
134 /* don't print ISR resets */
135 if (isr) {
136 IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n",
Andrew Jonesdbc464d2014-10-07 13:24:02 +0200137 isr ? 1 : 0, s->intrstatus, s->intrmask);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600138 }
139
Marcel Apfelbaum9e64f8a2013-10-07 10:36:39 +0300140 pci_set_irq(d, (isr != 0));
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600141}
142
143static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
144{
145 IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
146
147 s->intrmask = val;
148
Marc-André Lureaud8a5da02015-06-18 15:00:52 +0200149 ivshmem_update_irq(s);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600150}
151
152static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
153{
154 uint32_t ret = s->intrmask;
155
156 IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret);
157
158 return ret;
159}
160
161static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
162{
163 IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val);
164
165 s->intrstatus = val;
166
Marc-André Lureaud8a5da02015-06-18 15:00:52 +0200167 ivshmem_update_irq(s);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600168}
169
170static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
171{
172 uint32_t ret = s->intrstatus;
173
174 /* reading ISR clears all interrupts */
175 s->intrstatus = 0;
176
Marc-André Lureaud8a5da02015-06-18 15:00:52 +0200177 ivshmem_update_irq(s);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600178
179 return ret;
180}
181
Avi Kivitya8170e52012-10-23 12:30:10 +0200182static void ivshmem_io_write(void *opaque, hwaddr addr,
Avi Kivitycb066082011-08-08 16:09:12 +0300183 uint64_t val, unsigned size)
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600184{
185 IVShmemState *s = opaque;
186
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600187 uint16_t dest = val >> 16;
188 uint16_t vector = val & 0xff;
189
190 addr &= 0xfc;
191
192 IVSHMEM_DPRINTF("writing to addr " TARGET_FMT_plx "\n", addr);
193 switch (addr)
194 {
195 case INTRMASK:
196 ivshmem_IntrMask_write(s, val);
197 break;
198
199 case INTRSTATUS:
200 ivshmem_IntrStatus_write(s, val);
201 break;
202
203 case DOORBELL:
204 /* check that dest VM ID is reasonable */
Marc-André Lureau95c84252015-06-19 12:17:26 +0200205 if (dest >= s->nb_peers) {
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600206 IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest);
207 break;
208 }
209
210 /* check doorbell range */
Jes Sorensen1b27d7a2010-08-30 12:31:33 +0200211 if (vector < s->peers[dest].nb_eventfds) {
Paolo Bonzini563027c2012-07-05 17:16:25 +0200212 IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector);
213 event_notifier_set(&s->peers[dest].eventfds[vector]);
Marc-André Lureauf59bb372015-06-18 15:04:13 +0200214 } else {
215 IVSHMEM_DPRINTF("Invalid destination vector %d on VM %d\n",
216 vector, dest);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600217 }
218 break;
219 default:
Marc-André Lureauf59bb372015-06-18 15:04:13 +0200220 IVSHMEM_DPRINTF("Unhandled write " TARGET_FMT_plx "\n", addr);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600221 }
222}
223
Avi Kivitya8170e52012-10-23 12:30:10 +0200224static uint64_t ivshmem_io_read(void *opaque, hwaddr addr,
Avi Kivitycb066082011-08-08 16:09:12 +0300225 unsigned size)
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600226{
227
228 IVShmemState *s = opaque;
229 uint32_t ret;
230
231 switch (addr)
232 {
233 case INTRMASK:
234 ret = ivshmem_IntrMask_read(s);
235 break;
236
237 case INTRSTATUS:
238 ret = ivshmem_IntrStatus_read(s);
239 break;
240
241 case IVPOSITION:
242 /* return my VM ID if the memory is mapped */
Marc-André Lureauf689d282015-06-30 00:04:19 +0200243 if (memory_region_is_mapped(&s->ivshmem)) {
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600244 ret = s->vm_id;
245 } else {
246 ret = -1;
247 }
248 break;
249
250 default:
251 IVSHMEM_DPRINTF("why are we reading " TARGET_FMT_plx "\n", addr);
252 ret = 0;
253 }
254
255 return ret;
256}
257
Avi Kivitycb066082011-08-08 16:09:12 +0300258static const MemoryRegionOps ivshmem_mmio_ops = {
259 .read = ivshmem_io_read,
260 .write = ivshmem_io_write,
261 .endianness = DEVICE_NATIVE_ENDIAN,
262 .impl = {
263 .min_access_size = 4,
264 .max_access_size = 4,
265 },
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600266};
267
268static void ivshmem_receive(void *opaque, const uint8_t *buf, int size)
269{
270 IVShmemState *s = opaque;
271
Marc-André Lureauf59bb372015-06-18 15:04:13 +0200272 IVSHMEM_DPRINTF("ivshmem_receive 0x%02x size: %d\n", *buf, size);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600273
Marc-André Lureauf59bb372015-06-18 15:04:13 +0200274 ivshmem_IntrStatus_write(s, *buf);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600275}
276
277static int ivshmem_can_receive(void * opaque)
278{
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200279 return sizeof(int64_t);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600280}
281
282static void ivshmem_event(void *opaque, int event)
283{
284 IVSHMEM_DPRINTF("ivshmem_event %d\n", event);
285}
286
287static void fake_irqfd(void *opaque, const uint8_t *buf, int size) {
288
Marc-André Lureau0f573502015-07-27 12:59:19 +0200289 MSIVector *entry = opaque;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600290 PCIDevice *pdev = entry->pdev;
Marc-André Lureaud160f3f2015-07-24 18:52:19 +0200291 IVShmemState *s = IVSHMEM(pdev);
Marc-André Lureau0f573502015-07-27 12:59:19 +0200292 int vector = entry - s->msi_vectors;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600293
Marc-André Lureaud160f3f2015-07-24 18:52:19 +0200294 IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector);
295 msix_notify(pdev, vector);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600296}
297
Marc-André Lureau660c97e2015-07-09 15:50:13 +0200298static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector,
299 MSIMessage msg)
300{
301 IVShmemState *s = IVSHMEM(dev);
302 EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
303 MSIVector *v = &s->msi_vectors[vector];
304 int ret;
305
306 IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector);
307
308 ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev);
309 if (ret < 0) {
310 return ret;
311 }
312
313 return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq);
314}
315
316static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector)
317{
318 IVShmemState *s = IVSHMEM(dev);
319 EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
320 int ret;
321
322 IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector);
323
324 ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n,
325 s->msi_vectors[vector].virq);
326 if (ret != 0) {
327 error_report("remove_irqfd_notifier_gsi failed");
328 }
329}
330
331static void ivshmem_vector_poll(PCIDevice *dev,
332 unsigned int vector_start,
333 unsigned int vector_end)
334{
335 IVShmemState *s = IVSHMEM(dev);
336 unsigned int vector;
337
338 IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end);
339
340 vector_end = MIN(vector_end, s->vectors);
341
342 for (vector = vector_start; vector < vector_end; vector++) {
343 EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector];
344
345 if (!msix_is_masked(dev, vector)) {
346 continue;
347 }
348
349 if (event_notifier_test_and_clear(notifier)) {
350 msix_set_pending(dev, vector);
351 }
352 }
353}
354
Paolo Bonzini563027c2012-07-05 17:16:25 +0200355static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *n,
356 int vector)
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600357{
358 /* create a event character device based on the passed eventfd */
359 IVShmemState *s = opaque;
Marc-André Lureau660c97e2015-07-09 15:50:13 +0200360 PCIDevice *pdev = PCI_DEVICE(s);
Paolo Bonzini563027c2012-07-05 17:16:25 +0200361 int eventfd = event_notifier_get_fd(n);
Marc-André Lureau660c97e2015-07-09 15:50:13 +0200362 CharDriverState *chr;
363
364 s->msi_vectors[vector].pdev = pdev;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600365
366 chr = qemu_chr_open_eventfd(eventfd);
367
368 if (chr == NULL) {
Marc-André Lureau36617792015-06-18 14:39:49 +0200369 error_report("creating chardriver for eventfd %d failed", eventfd);
Marc-André Lureau03977ad2015-06-22 12:55:16 +0200370 return NULL;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600371 }
Hans de Goede456d6062013-03-27 20:29:40 +0100372 qemu_chr_fe_claim_no_fail(chr);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600373
374 /* if MSI is supported we need multiple interrupts */
375 if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
Marc-André Lureau0f573502015-07-27 12:59:19 +0200376 s->msi_vectors[vector].pdev = PCI_DEVICE(s);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600377
378 qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd,
Marc-André Lureau0f573502015-07-27 12:59:19 +0200379 ivshmem_event, &s->msi_vectors[vector]);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600380 } else {
381 qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive,
382 ivshmem_event, s);
383 }
384
385 return chr;
386
387}
388
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200389static int check_shm_size(IVShmemState *s, int fd, Error **errp)
390{
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600391 /* check that the guest isn't going to try and map more memory than the
392 * the object has allocated return -1 to indicate error */
393
394 struct stat buf;
395
zhanghailiang5edbdbc2014-08-14 15:29:15 +0800396 if (fstat(fd, &buf) < 0) {
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200397 error_setg(errp, "exiting: fstat on fd %d failed: %s",
398 fd, strerror(errno));
zhanghailiang5edbdbc2014-08-14 15:29:15 +0800399 return -1;
400 }
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600401
402 if (s->ivshmem_size > buf.st_size) {
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200403 error_setg(errp, "Requested memory size greater"
404 " than shared object size (%" PRIu64 " > %" PRIu64")",
405 s->ivshmem_size, (uint64_t)buf.st_size);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600406 return -1;
407 } else {
408 return 0;
409 }
410}
411
412/* create the shared memory BAR when we are not using the server, so we can
413 * create the BAR and map the memory immediately */
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200414static int create_shared_memory_BAR(IVShmemState *s, int fd, uint8_t attr,
415 Error **errp)
416{
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600417 void * ptr;
418
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600419 ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200420 if (ptr == MAP_FAILED) {
421 error_setg_errno(errp, errno, "Failed to mmap shared memory");
422 return -1;
423 }
424
Paolo Bonzini3c161542013-06-06 21:25:08 -0400425 memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), "ivshmem.bar2",
Avi Kivitycb066082011-08-08 16:09:12 +0300426 s->ivshmem_size, ptr);
Peter Crosthwaiteeb3fedf2013-06-24 16:59:29 +1000427 vmstate_register_ram(&s->ivshmem, DEVICE(s));
Avi Kivitycb066082011-08-08 16:09:12 +0300428 memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600429
430 /* region for shared memory */
Marc-André Lureau9113e3f2015-06-18 16:24:33 +0200431 pci_register_bar(PCI_DEVICE(s), 2, attr, &s->bar);
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200432
433 return 0;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600434}
435
Paolo Bonzini563027c2012-07-05 17:16:25 +0200436static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i)
437{
438 memory_region_add_eventfd(&s->ivshmem_mmio,
439 DOORBELL,
440 4,
441 true,
442 (posn << 16) | i,
Paolo Bonzini753d5e12012-07-05 17:16:27 +0200443 &s->peers[posn].eventfds[i]);
Paolo Bonzini563027c2012-07-05 17:16:25 +0200444}
445
446static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i)
447{
448 memory_region_del_eventfd(&s->ivshmem_mmio,
449 DOORBELL,
450 4,
451 true,
452 (posn << 16) | i,
Paolo Bonzini753d5e12012-07-05 17:16:27 +0200453 &s->peers[posn].eventfds[i]);
Paolo Bonzini563027c2012-07-05 17:16:25 +0200454}
455
Marc-André Lureauf4561792015-06-23 13:38:46 +0200456static void close_peer_eventfds(IVShmemState *s, int posn)
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600457{
Marc-André Lureauf4561792015-06-23 13:38:46 +0200458 int i, n;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600459
Paolo Bonzini98609cd2012-08-22 23:09:47 +0200460 if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
461 return;
462 }
Stefan Hajnoczi363ba1c2014-09-15 18:40:06 +0200463 if (posn < 0 || posn >= s->nb_peers) {
Marc-André Lureauffa99af2015-06-23 13:34:09 +0200464 error_report("invalid peer %d", posn);
Stefan Hajnoczi363ba1c2014-09-15 18:40:06 +0200465 return;
466 }
Paolo Bonzini98609cd2012-08-22 23:09:47 +0200467
Marc-André Lureauf4561792015-06-23 13:38:46 +0200468 n = s->peers[posn].nb_eventfds;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600469
Paolo Bonzinib6a1f3a2012-07-05 17:16:26 +0200470 memory_region_transaction_begin();
Marc-André Lureauf4561792015-06-23 13:38:46 +0200471 for (i = 0; i < n; i++) {
Paolo Bonzini563027c2012-07-05 17:16:25 +0200472 ivshmem_del_eventfd(s, posn, i);
Paolo Bonzinib6a1f3a2012-07-05 17:16:26 +0200473 }
474 memory_region_transaction_commit();
Marc-André Lureauf4561792015-06-23 13:38:46 +0200475 for (i = 0; i < n; i++) {
Paolo Bonzini563027c2012-07-05 17:16:25 +0200476 event_notifier_cleanup(&s->peers[posn].eventfds[i]);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600477 }
478
Anthony Liguori7267c092011-08-20 22:09:37 -0500479 g_free(s->peers[posn].eventfds);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600480 s->peers[posn].nb_eventfds = 0;
481}
482
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600483/* this function increase the dynamic storage need to store data about other
Marc-André Lureauf4561792015-06-23 13:38:46 +0200484 * peers */
Marc-André Lureau1300b272015-09-15 17:21:37 +0200485static int resize_peers(IVShmemState *s, int new_min_size)
Sebastian Krahmer34bc07c2014-09-15 18:40:07 +0200486{
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600487
Marc-André Lureau1300b272015-09-15 17:21:37 +0200488 int j, old_size;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600489
Marc-André Lureau61ea2d82015-09-15 16:55:10 +0200490 /* limit number of max peers */
491 if (new_min_size <= 0 || new_min_size > IVSHMEM_MAX_PEERS) {
Sebastian Krahmer34bc07c2014-09-15 18:40:07 +0200492 return -1;
493 }
Marc-André Lureau1300b272015-09-15 17:21:37 +0200494 if (new_min_size <= s->nb_peers) {
Sebastian Krahmer34bc07c2014-09-15 18:40:07 +0200495 return 0;
496 }
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600497
Marc-André Lureau1300b272015-09-15 17:21:37 +0200498 old_size = s->nb_peers;
499 s->nb_peers = new_min_size;
500
Marc-André Lureauf4561792015-06-23 13:38:46 +0200501 IVSHMEM_DPRINTF("bumping storage to %d peers\n", s->nb_peers);
Marc-André Lureau1300b272015-09-15 17:21:37 +0200502
Anthony Liguori7267c092011-08-20 22:09:37 -0500503 s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer));
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600504
Marc-André Lureau1300b272015-09-15 17:21:37 +0200505 for (j = old_size; j < s->nb_peers; j++) {
Marc-André Lureau81e507f2015-09-15 17:23:07 +0200506 s->peers[j].eventfds = g_new0(EventNotifier, s->vectors);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600507 s->peers[j].nb_eventfds = 0;
508 }
Sebastian Krahmer34bc07c2014-09-15 18:40:07 +0200509
510 return 0;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600511}
512
Marc-André Lureau0f14fd72015-06-23 17:56:37 +0200513static bool fifo_update_and_get(IVShmemState *s, const uint8_t *buf, int size,
514 void *data, size_t len)
515{
516 const uint8_t *p;
517 uint32_t num;
518
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200519 assert(len <= sizeof(int64_t)); /* limitation of the fifo */
Marc-André Lureau0f14fd72015-06-23 17:56:37 +0200520 if (fifo8_is_empty(&s->incoming_fifo) && size == len) {
521 memcpy(data, buf, size);
522 return true;
523 }
524
525 IVSHMEM_DPRINTF("short read of %d bytes\n", size);
526
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200527 num = MIN(size, sizeof(int64_t) - fifo8_num_used(&s->incoming_fifo));
Marc-André Lureau0f14fd72015-06-23 17:56:37 +0200528 fifo8_push_all(&s->incoming_fifo, buf, num);
529
530 if (fifo8_num_used(&s->incoming_fifo) < len) {
531 assert(num == 0);
532 return false;
533 }
534
535 size -= num;
536 buf += num;
537 p = fifo8_pop_buf(&s->incoming_fifo, len, &num);
538 assert(num == len);
539
540 memcpy(data, p, len);
541
542 if (size > 0) {
543 fifo8_push_all(&s->incoming_fifo, buf, size);
544 }
545
546 return true;
547}
548
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200549static bool fifo_update_and_get_i64(IVShmemState *s,
550 const uint8_t *buf, int size, int64_t *i64)
551{
552 if (fifo_update_and_get(s, buf, size, i64, sizeof(*i64))) {
553 *i64 = GINT64_FROM_LE(*i64);
554 return true;
555 }
556
557 return false;
558}
559
Marc-André Lureau660c97e2015-07-09 15:50:13 +0200560static int ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector)
561{
562 PCIDevice *pdev = PCI_DEVICE(s);
563 MSIMessage msg = msix_get_message(pdev, vector);
564 int ret;
565
566 IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector);
567
568 if (s->msi_vectors[vector].pdev != NULL) {
569 return 0;
570 }
571
572 ret = kvm_irqchip_add_msi_route(kvm_state, msg, pdev);
573 if (ret < 0) {
574 error_report("ivshmem: kvm_irqchip_add_msi_route failed");
575 return -1;
576 }
577
578 s->msi_vectors[vector].virq = ret;
579 s->msi_vectors[vector].pdev = pdev;
580
581 return 0;
582}
583
584static void setup_interrupt(IVShmemState *s, int vector)
585{
586 EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
587 bool with_irqfd = kvm_msi_via_irqfd_enabled() &&
588 ivshmem_has_feature(s, IVSHMEM_MSI);
589 PCIDevice *pdev = PCI_DEVICE(s);
590
591 IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector);
592
593 if (!with_irqfd) {
594 IVSHMEM_DPRINTF("with eventfd");
595 s->eventfd_chr[vector] = create_eventfd_chr_device(s, n, vector);
596 } else if (msix_enabled(pdev)) {
597 IVSHMEM_DPRINTF("with irqfd");
598 if (ivshmem_add_kvm_msi_virq(s, vector) < 0) {
599 return;
600 }
601
602 if (!msix_is_masked(pdev, vector)) {
603 kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL,
604 s->msi_vectors[vector].virq);
605 }
606 } else {
607 /* it will be delayed until msix is enabled, in write_config */
608 IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled");
609 }
610}
611
Stefan Hajnoczia2e90112014-09-15 18:40:05 +0200612static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600613{
614 IVShmemState *s = opaque;
Marc-André Lureaudee21512015-06-22 12:38:34 +0200615 int incoming_fd;
Marc-André Lureau9a2f0e62015-06-19 12:19:55 +0200616 int new_eventfd;
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200617 int64_t incoming_posn;
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200618 Error *err = NULL;
Marc-André Lureau9a2f0e62015-06-19 12:19:55 +0200619 Peer *peer;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600620
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200621 if (!fifo_update_and_get_i64(s, buf, size, &incoming_posn)) {
Marc-André Lureau0f14fd72015-06-23 17:56:37 +0200622 return;
Stefan Hajnoczia2e90112014-09-15 18:40:05 +0200623 }
624
Stefan Hajnoczi363ba1c2014-09-15 18:40:06 +0200625 if (incoming_posn < -1) {
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200626 IVSHMEM_DPRINTF("invalid incoming_posn %" PRId64 "\n", incoming_posn);
Stefan Hajnoczi363ba1c2014-09-15 18:40:06 +0200627 return;
628 }
629
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600630 /* pick off s->server_chr->msgfd and store it, posn should accompany msg */
Marc-André Lureaudee21512015-06-22 12:38:34 +0200631 incoming_fd = qemu_chr_fe_get_msgfd(s->server_chr);
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200632 IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n",
633 incoming_posn, incoming_fd);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600634
Marc-André Lureauf4561792015-06-23 13:38:46 +0200635 /* make sure we have enough space for this peer */
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600636 if (incoming_posn >= s->nb_peers) {
Marc-André Lureau1300b272015-09-15 17:21:37 +0200637 if (resize_peers(s, incoming_posn + 1) < 0) {
638 error_report("failed to resize peers array");
Marc-André Lureaudee21512015-06-22 12:38:34 +0200639 if (incoming_fd != -1) {
640 close(incoming_fd);
Sebastian Krahmer34bc07c2014-09-15 18:40:07 +0200641 }
642 return;
643 }
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600644 }
645
Marc-André Lureau9a2f0e62015-06-19 12:19:55 +0200646 peer = &s->peers[incoming_posn];
647
Marc-André Lureaudee21512015-06-22 12:38:34 +0200648 if (incoming_fd == -1) {
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600649 /* if posn is positive and unseen before then this is our posn*/
Marc-André Lureau81e507f2015-09-15 17:23:07 +0200650 if (incoming_posn >= 0 && s->vm_id == -1) {
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600651 /* receive our posn */
652 s->vm_id = incoming_posn;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600653 } else {
Marc-André Lureauf4561792015-06-23 13:38:46 +0200654 /* otherwise an fd == -1 means an existing peer has gone away */
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200655 IVSHMEM_DPRINTF("posn %" PRId64 " has gone away\n", incoming_posn);
Marc-André Lureauf4561792015-06-23 13:38:46 +0200656 close_peer_eventfds(s, incoming_posn);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600657 }
Marc-André Lureau6f8a16d2015-06-19 12:21:46 +0200658 return;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600659 }
660
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600661 /* if the position is -1, then it's shared memory region fd */
662 if (incoming_posn == -1) {
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600663 void * map_ptr;
664
Marc-André Lureauf689d282015-06-30 00:04:19 +0200665 if (memory_region_is_mapped(&s->ivshmem)) {
Marc-André Lureau945001a2015-06-23 12:55:41 +0200666 error_report("shm already initialized");
667 close(incoming_fd);
668 return;
669 }
670
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200671 if (check_shm_size(s, incoming_fd, &err) == -1) {
672 error_report_err(err);
673 close(incoming_fd);
674 return;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600675 }
676
677 /* mmap the region and map into the BAR2 */
678 map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED,
679 incoming_fd, 0);
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200680 if (map_ptr == MAP_FAILED) {
681 error_report("Failed to mmap shared memory %s", strerror(errno));
682 close(incoming_fd);
683 return;
684 }
Paolo Bonzini3c161542013-06-06 21:25:08 -0400685 memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s),
Avi Kivitycb066082011-08-08 16:09:12 +0300686 "ivshmem.bar2", s->ivshmem_size, map_ptr);
Peter Crosthwaiteeb3fedf2013-06-24 16:59:29 +1000687 vmstate_register_ram(&s->ivshmem, DEVICE(s));
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600688
Levente Kurusa7f9efb62014-08-04 17:06:20 +0200689 IVSHMEM_DPRINTF("guest h/w addr = %p, size = %" PRIu64 "\n",
Andrew Jonesdbc464d2014-10-07 13:24:02 +0200690 map_ptr, s->ivshmem_size);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600691
Avi Kivitycb066082011-08-08 16:09:12 +0300692 memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600693
Marc-André Lureauf689d282015-06-30 00:04:19 +0200694 close(incoming_fd);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600695 return;
696 }
697
Marc-André Lureau9a2f0e62015-06-19 12:19:55 +0200698 /* each peer has an associated array of eventfds, and we keep
699 * track of how many eventfds received so far */
700 /* get a new eventfd: */
Marc-André Lureau1ee57de2015-06-23 14:07:11 +0200701 if (peer->nb_eventfds >= s->vectors) {
702 error_report("Too many eventfd received, device has %d vectors",
703 s->vectors);
704 close(incoming_fd);
705 return;
706 }
707
Marc-André Lureau9a2f0e62015-06-19 12:19:55 +0200708 new_eventfd = peer->nb_eventfds++;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600709
Marc-André Lureauf4561792015-06-23 13:38:46 +0200710 /* this is an eventfd for a particular peer VM */
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200711 IVSHMEM_DPRINTF("eventfds[%" PRId64 "][%d] = %d\n", incoming_posn,
Marc-André Lureau9a2f0e62015-06-19 12:19:55 +0200712 new_eventfd, incoming_fd);
713 event_notifier_init_fd(&peer->eventfds[new_eventfd], incoming_fd);
Marc-André Lureau660c97e2015-07-09 15:50:13 +0200714 fcntl_setfl(incoming_fd, O_NONBLOCK); /* msix/irqfd poll non block */
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600715
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600716 if (incoming_posn == s->vm_id) {
Marc-André Lureau660c97e2015-07-09 15:50:13 +0200717 setup_interrupt(s, new_eventfd);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600718 }
719
720 if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
Marc-André Lureau9a2f0e62015-06-19 12:19:55 +0200721 ivshmem_add_eventfd(s, incoming_posn, new_eventfd);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600722 }
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600723}
724
David Marchand5105b1d2015-06-16 17:43:34 +0200725static void ivshmem_check_version(void *opaque, const uint8_t * buf, int size)
726{
727 IVShmemState *s = opaque;
728 int tmp;
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200729 int64_t version;
David Marchand5105b1d2015-06-16 17:43:34 +0200730
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200731 if (!fifo_update_and_get_i64(s, buf, size, &version)) {
David Marchand5105b1d2015-06-16 17:43:34 +0200732 return;
733 }
734
735 tmp = qemu_chr_fe_get_msgfd(s->server_chr);
736 if (tmp != -1 || version != IVSHMEM_PROTOCOL_VERSION) {
737 fprintf(stderr, "incompatible version, you are connecting to a ivshmem-"
738 "server using a different protocol please check your setup\n");
739 qemu_chr_delete(s->server_chr);
740 s->server_chr = NULL;
741 return;
742 }
743
744 IVSHMEM_DPRINTF("version check ok, switch to real chardev handler\n");
745 qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read,
746 ivshmem_event, s);
747}
748
Michael S. Tsirkin4490c712011-12-05 21:48:43 +0200749/* Select the MSI-X vectors used by device.
750 * ivshmem maps events to vectors statically, so
751 * we just enable all vectors on init and after reset. */
752static void ivshmem_use_msix(IVShmemState * s)
753{
Andreas Färberb7578ea2013-06-30 15:15:15 +0200754 PCIDevice *d = PCI_DEVICE(s);
Michael S. Tsirkin4490c712011-12-05 21:48:43 +0200755 int i;
756
Marc-André Lureauf59bb372015-06-18 15:04:13 +0200757 IVSHMEM_DPRINTF("%s, msix present: %d\n", __func__, msix_present(d));
Andreas Färberb7578ea2013-06-30 15:15:15 +0200758 if (!msix_present(d)) {
Michael S. Tsirkin4490c712011-12-05 21:48:43 +0200759 return;
760 }
761
762 for (i = 0; i < s->vectors; i++) {
Andreas Färberb7578ea2013-06-30 15:15:15 +0200763 msix_vector_use(d, i);
Michael S. Tsirkin4490c712011-12-05 21:48:43 +0200764 }
765}
766
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600767static void ivshmem_reset(DeviceState *d)
768{
Peter Crosthwaiteeb3fedf2013-06-24 16:59:29 +1000769 IVShmemState *s = IVSHMEM(d);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600770
771 s->intrstatus = 0;
Marc-André Lureau972ad212015-06-23 14:13:08 +0200772 s->intrmask = 0;
Michael S. Tsirkin4490c712011-12-05 21:48:43 +0200773 ivshmem_use_msix(s);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600774}
775
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200776static int ivshmem_setup_msi(IVShmemState * s)
Michael S. Tsirkin4490c712011-12-05 21:48:43 +0200777{
Andreas Färberb7578ea2013-06-30 15:15:15 +0200778 if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) {
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200779 return -1;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600780 }
781
Alex Williamson1116b532012-06-14 12:16:01 -0600782 IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors);
783
Stefan Weil5cbdb3a2012-04-07 09:23:39 +0200784 /* allocate QEMU char devices for receiving interrupts */
Marc-André Lureau0f573502015-07-27 12:59:19 +0200785 s->msi_vectors = g_malloc0(s->vectors * sizeof(MSIVector));
Michael S. Tsirkin4490c712011-12-05 21:48:43 +0200786
787 ivshmem_use_msix(s);
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200788 return 0;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600789}
790
Marc-André Lureau660c97e2015-07-09 15:50:13 +0200791static void ivshmem_enable_irqfd(IVShmemState *s)
792{
793 PCIDevice *pdev = PCI_DEVICE(s);
794 int i;
795
796 for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
797 ivshmem_add_kvm_msi_virq(s, i);
798 }
799
800 if (msix_set_vector_notifiers(pdev,
801 ivshmem_vector_unmask,
802 ivshmem_vector_mask,
803 ivshmem_vector_poll)) {
804 error_report("ivshmem: msix_set_vector_notifiers failed");
805 }
806}
807
808static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector)
809{
810 IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector);
811
812 if (s->msi_vectors[vector].pdev == NULL) {
813 return;
814 }
815
816 /* it was cleaned when masked in the frontend. */
817 kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq);
818
819 s->msi_vectors[vector].pdev = NULL;
820}
821
822static void ivshmem_disable_irqfd(IVShmemState *s)
823{
824 PCIDevice *pdev = PCI_DEVICE(s);
825 int i;
826
827 for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
828 ivshmem_remove_kvm_msi_virq(s, i);
829 }
830
831 msix_unset_vector_notifiers(pdev);
832}
833
834static void ivshmem_write_config(PCIDevice *pdev, uint32_t address,
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200835 uint32_t val, int len)
Michael S. Tsirkin4490c712011-12-05 21:48:43 +0200836{
Marc-André Lureau660c97e2015-07-09 15:50:13 +0200837 IVShmemState *s = IVSHMEM(pdev);
838 int is_enabled, was_enabled = msix_enabled(pdev);
839
840 pci_default_write_config(pdev, address, val, len);
841 is_enabled = msix_enabled(pdev);
842
843 if (kvm_msi_via_irqfd_enabled() && s->vm_id != -1) {
844 if (!was_enabled && is_enabled) {
845 ivshmem_enable_irqfd(s);
846 } else if (was_enabled && !is_enabled) {
847 ivshmem_disable_irqfd(s);
848 }
849 }
Michael S. Tsirkin4490c712011-12-05 21:48:43 +0200850}
851
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200852static void pci_ivshmem_realize(PCIDevice *dev, Error **errp)
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600853{
Peter Crosthwaiteeb3fedf2013-06-24 16:59:29 +1000854 IVShmemState *s = IVSHMEM(dev);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600855 uint8_t *pci_conf;
Marc-André Lureau9113e3f2015-06-18 16:24:33 +0200856 uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
857 PCI_BASE_ADDRESS_MEM_PREFETCH;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600858
Marc-André Lureaud9453c92015-06-30 00:10:16 +0200859 if (!!s->server_chr + !!s->shmobj + !!s->hostmem != 1) {
860 error_setg(errp, "You must specify either a shmobj, a chardev"
861 " or a hostmem");
862 return;
863 }
864
865 if (s->hostmem) {
866 MemoryRegion *mr;
867
868 if (s->sizearg) {
869 g_warning("size argument ignored with hostmem");
870 }
871
872 mr = host_memory_backend_get_memory(s->hostmem, errp);
873 s->ivshmem_size = memory_region_size(mr);
874 } else if (s->sizearg == NULL) {
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600875 s->ivshmem_size = 4 << 20; /* 4 MB default */
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200876 } else {
Marc-André Lureau2c047522015-06-30 00:06:03 +0200877 char *end;
878 int64_t size = qemu_strtosz(s->sizearg, &end);
879 if (size < 0 || *end != '\0' || !is_power_of_2(size)) {
880 error_setg(errp, "Invalid size %s", s->sizearg);
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200881 return;
882 }
Marc-André Lureau2c047522015-06-30 00:06:03 +0200883 s->ivshmem_size = size;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600884 }
885
Marc-André Lureauf7a199b2015-09-24 12:55:01 +0200886 fifo8_create(&s->incoming_fifo, sizeof(int64_t));
Marc-André Lureau1f8552d2015-06-18 14:05:46 +0200887
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600888 /* IRQFD requires MSI */
889 if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
890 !ivshmem_has_feature(s, IVSHMEM_MSI)) {
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200891 error_setg(errp, "ioeventfd/irqfd requires MSI");
892 return;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600893 }
894
895 /* check that role is reasonable */
896 if (s->role) {
897 if (strncmp(s->role, "peer", 5) == 0) {
898 s->role_val = IVSHMEM_PEER;
899 } else if (strncmp(s->role, "master", 7) == 0) {
900 s->role_val = IVSHMEM_MASTER;
901 } else {
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200902 error_setg(errp, "'role' must be 'peer' or 'master'");
903 return;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600904 }
905 } else {
906 s->role_val = IVSHMEM_MASTER; /* default */
907 }
908
909 if (s->role_val == IVSHMEM_PEER) {
Cole Robinsonf231b882014-03-21 19:42:26 -0400910 error_setg(&s->migration_blocker,
911 "Migration is disabled when using feature 'peer mode' in device 'ivshmem'");
Anthony Liguori38e07352011-11-14 15:09:44 -0600912 migrate_add_blocker(s->migration_blocker);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600913 }
914
Andreas Färberb7578ea2013-06-30 15:15:15 +0200915 pci_conf = dev->config;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600916 pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600917
918 pci_config_set_interrupt_pin(pci_conf, 1);
919
Paolo Bonzini3c161542013-06-06 21:25:08 -0400920 memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s,
Avi Kivitycb066082011-08-08 16:09:12 +0300921 "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE);
922
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600923 /* region for registers*/
Andreas Färberb7578ea2013-06-30 15:15:15 +0200924 pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
Avi Kivitye824b2c2011-08-08 16:09:31 +0300925 &s->ivshmem_mmio);
Avi Kivitycb066082011-08-08 16:09:12 +0300926
Paolo Bonzini3c161542013-06-06 21:25:08 -0400927 memory_region_init(&s->bar, OBJECT(s), "ivshmem-bar2-container", s->ivshmem_size);
Gerd Hoffmannc08ba662012-09-13 11:08:02 +0200928 if (s->ivshmem_64bit) {
Marc-André Lureau9113e3f2015-06-18 16:24:33 +0200929 attr |= PCI_BASE_ADDRESS_MEM_TYPE_64;
Gerd Hoffmannc08ba662012-09-13 11:08:02 +0200930 }
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600931
Marc-André Lureaud9453c92015-06-30 00:10:16 +0200932 if (s->hostmem != NULL) {
933 MemoryRegion *mr;
934
935 IVSHMEM_DPRINTF("using hostmem\n");
936
937 mr = host_memory_backend_get_memory(MEMORY_BACKEND(s->hostmem), errp);
938 vmstate_register_ram(mr, DEVICE(s));
939 memory_region_add_subregion(&s->bar, 0, mr);
940 pci_register_bar(PCI_DEVICE(s), 2, attr, &s->bar);
941 } else if (s->server_chr != NULL) {
Marc-André Lureau36617792015-06-18 14:39:49 +0200942 if (strncmp(s->server_chr->filename, "unix:", 5)) {
943 error_setg(errp, "chardev is not a unix client socket");
944 return;
945 }
946
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600947 /* if we get a UNIX socket as the parameter we will talk
948 * to the ivshmem server to receive the memory region */
949
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600950 IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
Andrew Jonesdbc464d2014-10-07 13:24:02 +0200951 s->server_chr->filename);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600952
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200953 if (ivshmem_has_feature(s, IVSHMEM_MSI) &&
954 ivshmem_setup_msi(s)) {
955 error_setg(errp, "msix initialization failed");
956 return;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600957 }
958
Marc-André Lureauf4561792015-06-23 13:38:46 +0200959 /* we allocate enough space for 16 peers and grow as needed */
Marc-André Lureau1300b272015-09-15 17:21:37 +0200960 resize_peers(s, 16);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600961 s->vm_id = -1;
962
Marc-André Lureau9113e3f2015-06-18 16:24:33 +0200963 pci_register_bar(dev, 2, attr, &s->bar);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600964
Anthony Liguori7267c092011-08-20 22:09:37 -0500965 s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *));
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600966
David Marchand5105b1d2015-06-16 17:43:34 +0200967 qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive,
968 ivshmem_check_version, ivshmem_event, s);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600969 } else {
970 /* just map the file immediately, we're not using a server */
971 int fd;
972
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600973 IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj);
974
975 /* try opening with O_EXCL and if it succeeds zero the memory
976 * by truncating to 0 */
977 if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL,
978 S_IRWXU|S_IRWXG|S_IRWXO)) > 0) {
979 /* truncate file to length PCI device's memory */
980 if (ftruncate(fd, s->ivshmem_size) != 0) {
Andrew Jonesdbc464d2014-10-07 13:24:02 +0200981 error_report("could not truncate shared file");
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600982 }
983
984 } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR,
985 S_IRWXU|S_IRWXG|S_IRWXO)) < 0) {
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200986 error_setg(errp, "could not open shared file");
987 return;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600988 }
989
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200990 if (check_shm_size(s, fd, errp) == -1) {
991 return;
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600992 }
993
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200994 create_shared_memory_BAR(s, fd, attr, errp);
Marc-André Lureauf689d282015-06-30 00:04:19 +0200995 close(fd);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600996 }
Cam Macdonell6cbf4c82010-07-27 10:54:13 -0600997}
998
Marc-André Lureaud58d7e82015-06-18 14:59:28 +0200999static void pci_ivshmem_exit(PCIDevice *dev)
Cam Macdonell6cbf4c82010-07-27 10:54:13 -06001000{
Peter Crosthwaiteeb3fedf2013-06-24 16:59:29 +10001001 IVShmemState *s = IVSHMEM(dev);
Marc-André Lureauf64a0782015-06-23 12:57:16 +02001002 int i;
1003
1004 fifo8_destroy(&s->incoming_fifo);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -06001005
Anthony Liguori38e07352011-11-14 15:09:44 -06001006 if (s->migration_blocker) {
1007 migrate_del_blocker(s->migration_blocker);
1008 error_free(s->migration_blocker);
1009 }
1010
Marc-André Lureauf689d282015-06-30 00:04:19 +02001011 if (memory_region_is_mapped(&s->ivshmem)) {
Marc-André Lureaud9453c92015-06-30 00:10:16 +02001012 if (!s->hostmem) {
1013 void *addr = memory_region_get_ram_ptr(&s->ivshmem);
1014
1015 if (munmap(addr, s->ivshmem_size) == -1) {
1016 error_report("Failed to munmap shared memory %s",
1017 strerror(errno));
1018 }
1019 }
Marc-André Lureauf64a0782015-06-23 12:57:16 +02001020
1021 vmstate_unregister_ram(&s->ivshmem, DEVICE(dev));
1022 memory_region_del_subregion(&s->bar, &s->ivshmem);
Marc-André Lureauf64a0782015-06-23 12:57:16 +02001023 }
1024
1025 if (s->eventfd_chr) {
1026 for (i = 0; i < s->vectors; i++) {
1027 if (s->eventfd_chr[i]) {
1028 qemu_chr_free(s->eventfd_chr[i]);
1029 }
1030 }
1031 g_free(s->eventfd_chr);
1032 }
1033
1034 if (s->peers) {
1035 for (i = 0; i < s->nb_peers; i++) {
Marc-André Lureauf4561792015-06-23 13:38:46 +02001036 close_peer_eventfds(s, i);
Marc-André Lureauf64a0782015-06-23 12:57:16 +02001037 }
1038 g_free(s->peers);
1039 }
1040
1041 if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
1042 msix_uninit_exclusive_bar(dev);
1043 }
1044
Marc-André Lureau0f573502015-07-27 12:59:19 +02001045 g_free(s->msi_vectors);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -06001046}
1047
Marc-André Lureau1f8552d2015-06-18 14:05:46 +02001048static bool test_msix(void *opaque, int version_id)
1049{
1050 IVShmemState *s = opaque;
1051
1052 return ivshmem_has_feature(s, IVSHMEM_MSI);
1053}
1054
1055static bool test_no_msix(void *opaque, int version_id)
1056{
1057 return !test_msix(opaque, version_id);
1058}
1059
1060static int ivshmem_pre_load(void *opaque)
1061{
1062 IVShmemState *s = opaque;
1063
1064 if (s->role_val == IVSHMEM_PEER) {
1065 error_report("'peer' devices are not migratable");
1066 return -EINVAL;
1067 }
1068
1069 return 0;
1070}
1071
1072static int ivshmem_post_load(void *opaque, int version_id)
1073{
1074 IVShmemState *s = opaque;
1075
1076 if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
1077 ivshmem_use_msix(s);
1078 }
1079
1080 return 0;
1081}
1082
1083static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id)
1084{
1085 IVShmemState *s = opaque;
1086 PCIDevice *pdev = PCI_DEVICE(s);
1087 int ret;
1088
1089 IVSHMEM_DPRINTF("ivshmem_load_old\n");
1090
1091 if (version_id != 0) {
1092 return -EINVAL;
1093 }
1094
1095 if (s->role_val == IVSHMEM_PEER) {
1096 error_report("'peer' devices are not migratable");
1097 return -EINVAL;
1098 }
1099
1100 ret = pci_device_load(pdev, f);
1101 if (ret) {
1102 return ret;
1103 }
1104
1105 if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
1106 msix_load(pdev, f);
1107 ivshmem_use_msix(s);
1108 } else {
1109 s->intrstatus = qemu_get_be32(f);
1110 s->intrmask = qemu_get_be32(f);
1111 }
1112
1113 return 0;
1114}
1115
1116static const VMStateDescription ivshmem_vmsd = {
1117 .name = "ivshmem",
1118 .version_id = 1,
1119 .minimum_version_id = 1,
1120 .pre_load = ivshmem_pre_load,
1121 .post_load = ivshmem_post_load,
1122 .fields = (VMStateField[]) {
1123 VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
1124
1125 VMSTATE_MSIX_TEST(parent_obj, IVShmemState, test_msix),
1126 VMSTATE_UINT32_TEST(intrstatus, IVShmemState, test_no_msix),
1127 VMSTATE_UINT32_TEST(intrmask, IVShmemState, test_no_msix),
1128
1129 VMSTATE_END_OF_LIST()
1130 },
1131 .load_state_old = ivshmem_load_old,
1132 .minimum_version_id_old = 0
1133};
1134
Anthony Liguori40021f02011-12-04 12:22:06 -06001135static Property ivshmem_properties[] = {
1136 DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
1137 DEFINE_PROP_STRING("size", IVShmemState, sizearg),
1138 DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
1139 DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false),
1140 DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true),
1141 DEFINE_PROP_STRING("shm", IVShmemState, shmobj),
1142 DEFINE_PROP_STRING("role", IVShmemState, role),
Gerd Hoffmannc08ba662012-09-13 11:08:02 +02001143 DEFINE_PROP_UINT32("use64", IVShmemState, ivshmem_64bit, 1),
Anthony Liguori40021f02011-12-04 12:22:06 -06001144 DEFINE_PROP_END_OF_LIST(),
1145};
1146
1147static void ivshmem_class_init(ObjectClass *klass, void *data)
1148{
Anthony Liguori39bffca2011-12-07 21:34:16 -06001149 DeviceClass *dc = DEVICE_CLASS(klass);
Anthony Liguori40021f02011-12-04 12:22:06 -06001150 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
1151
Marc-André Lureaud58d7e82015-06-18 14:59:28 +02001152 k->realize = pci_ivshmem_realize;
1153 k->exit = pci_ivshmem_exit;
1154 k->config_write = ivshmem_write_config;
Paolo Bonzinib8ef62a2012-12-13 10:19:37 +01001155 k->vendor_id = PCI_VENDOR_ID_IVSHMEM;
1156 k->device_id = PCI_DEVICE_ID_IVSHMEM;
Anthony Liguori40021f02011-12-04 12:22:06 -06001157 k->class_id = PCI_CLASS_MEMORY_RAM;
Anthony Liguori39bffca2011-12-07 21:34:16 -06001158 dc->reset = ivshmem_reset;
1159 dc->props = ivshmem_properties;
Marc-André Lureau1f8552d2015-06-18 14:05:46 +02001160 dc->vmsd = &ivshmem_vmsd;
Marcel Apfelbaum125ee0e2013-07-29 17:17:45 +03001161 set_bit(DEVICE_CATEGORY_MISC, dc->categories);
Marc-André Lureaud3835372015-06-23 13:01:40 +02001162 dc->desc = "Inter-VM shared memory";
Anthony Liguori40021f02011-12-04 12:22:06 -06001163}
1164
Marc-André Lureaud9453c92015-06-30 00:10:16 +02001165static void ivshmem_check_memdev_is_busy(Object *obj, const char *name,
1166 Object *val, Error **errp)
1167{
1168 MemoryRegion *mr;
1169
1170 mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), errp);
1171 if (memory_region_is_mapped(mr)) {
1172 char *path = object_get_canonical_path_component(val);
1173 error_setg(errp, "can't use already busy memdev: %s", path);
1174 g_free(path);
1175 } else {
1176 qdev_prop_allow_set_link_before_realize(obj, name, val, errp);
1177 }
1178}
1179
1180static void ivshmem_init(Object *obj)
1181{
1182 IVShmemState *s = IVSHMEM(obj);
1183
1184 object_property_add_link(obj, IVSHMEM_MEMDEV_PROP, TYPE_MEMORY_BACKEND,
1185 (Object **)&s->hostmem,
1186 ivshmem_check_memdev_is_busy,
1187 OBJ_PROP_LINK_UNREF_ON_RELEASE,
1188 &error_abort);
1189}
1190
Andreas Färber8c43a6f2013-01-10 16:19:07 +01001191static const TypeInfo ivshmem_info = {
Peter Crosthwaiteeb3fedf2013-06-24 16:59:29 +10001192 .name = TYPE_IVSHMEM,
Anthony Liguori39bffca2011-12-07 21:34:16 -06001193 .parent = TYPE_PCI_DEVICE,
1194 .instance_size = sizeof(IVShmemState),
Marc-André Lureaud9453c92015-06-30 00:10:16 +02001195 .instance_init = ivshmem_init,
Anthony Liguori39bffca2011-12-07 21:34:16 -06001196 .class_init = ivshmem_class_init,
Cam Macdonell6cbf4c82010-07-27 10:54:13 -06001197};
1198
Andreas Färber83f7d432012-02-09 15:20:55 +01001199static void ivshmem_register_types(void)
Cam Macdonell6cbf4c82010-07-27 10:54:13 -06001200{
Anthony Liguori39bffca2011-12-07 21:34:16 -06001201 type_register_static(&ivshmem_info);
Cam Macdonell6cbf4c82010-07-27 10:54:13 -06001202}
1203
Andreas Färber83f7d432012-02-09 15:20:55 +01001204type_init(ivshmem_register_types)