xref: /qemu/hw/misc/ivshmem-pci.c (revision e3d0814368d00e7985c31edf5d0cfce45972d4be)
16cbf4c8cSCam Macdonell /*
26cbf4c8cSCam Macdonell  * Inter-VM Shared Memory PCI device.
36cbf4c8cSCam Macdonell  *
46cbf4c8cSCam Macdonell  * Author:
56cbf4c8cSCam Macdonell  *      Cam Macdonell <cam@cs.ualberta.ca>
66cbf4c8cSCam Macdonell  *
76cbf4c8cSCam Macdonell  * Based On: cirrus_vga.c
86cbf4c8cSCam Macdonell  *          Copyright (c) 2004 Fabrice Bellard
96cbf4c8cSCam Macdonell  *          Copyright (c) 2004 Makoto Suzuki (suzu)
106cbf4c8cSCam Macdonell  *
116cbf4c8cSCam Macdonell  *      and rtl8139.c
126cbf4c8cSCam Macdonell  *          Copyright (c) 2006 Igor Kovalenko
136cbf4c8cSCam Macdonell  *
146cbf4c8cSCam Macdonell  * This code is licensed under the GNU GPL v2.
156b620ca3SPaolo Bonzini  *
166b620ca3SPaolo Bonzini  * Contributions after 2012-01-13 are licensed under the terms of the
176b620ca3SPaolo Bonzini  * GNU GPL, version 2 or (at your option) any later version.
186cbf4c8cSCam Macdonell  */
190b8fa32fSMarkus Armbruster 
200d1c9782SPeter Maydell #include "qemu/osdep.h"
21519abcdfSPhilippe Mathieu-Daudé #include "qemu/units.h"
22da34e65cSMarkus Armbruster #include "qapi/error.h"
23f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
2483c9f4caSPaolo Bonzini #include "hw/pci/pci.h"
25a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
26ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
27660c97eeSMarc-André Lureau #include "hw/pci/msi.h"
2883c9f4caSPaolo Bonzini #include "hw/pci/msix.h"
299c17d615SPaolo Bonzini #include "sysemu/kvm.h"
30795c40b8SJuan Quintela #include "migration/blocker.h"
31d6454270SMarkus Armbruster #include "migration/vmstate.h"
32d49b6836SMarkus Armbruster #include "qemu/error-report.h"
331de7afc9SPaolo Bonzini #include "qemu/event_notifier.h"
340b8fa32fSMarkus Armbruster #include "qemu/module.h"
355503e285SMarkus Armbruster #include "qom/object_interfaces.h"
364d43a603SMarc-André Lureau #include "chardev/char-fe.h"
37d9453c93SMarc-André Lureau #include "sysemu/hostmem.h"
38d9453c93SMarc-André Lureau #include "qapi/visitor.h"
396cbf4c8cSCam Macdonell 
405105b1d8SDavid Marchand #include "hw/misc/ivshmem.h"
41db1015e9SEduardo Habkost #include "qom/object.h"
425105b1d8SDavid Marchand 
43b8ef62a9SPaolo Bonzini #define PCI_VENDOR_ID_IVSHMEM   PCI_VENDOR_ID_REDHAT_QUMRANET
44b8ef62a9SPaolo Bonzini #define PCI_DEVICE_ID_IVSHMEM   0x1110
45b8ef62a9SPaolo Bonzini 
46cd9953f7SMarkus Armbruster #define IVSHMEM_MAX_PEERS UINT16_MAX
476cbf4c8cSCam Macdonell #define IVSHMEM_IOEVENTFD   0
486cbf4c8cSCam Macdonell #define IVSHMEM_MSI     1
496cbf4c8cSCam Macdonell 
506cbf4c8cSCam Macdonell #define IVSHMEM_REG_BAR_SIZE 0x100
516cbf4c8cSCam Macdonell 
52a4fa93bfSMarkus Armbruster #define IVSHMEM_DEBUG 0
536cbf4c8cSCam Macdonell #define IVSHMEM_DPRINTF(fmt, ...)                       \
54a4fa93bfSMarkus Armbruster     do {                                                \
55a4fa93bfSMarkus Armbruster         if (IVSHMEM_DEBUG) {                            \
56a4fa93bfSMarkus Armbruster             printf("IVSHMEM: " fmt, ## __VA_ARGS__);    \
57a4fa93bfSMarkus Armbruster         }                                               \
58a4fa93bfSMarkus Armbruster     } while (0)
596cbf4c8cSCam Macdonell 
605400c02bSMarkus Armbruster #define TYPE_IVSHMEM_COMMON "ivshmem-common"
61db1015e9SEduardo Habkost typedef struct IVShmemState IVShmemState;
628110fa1dSEduardo Habkost DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM_COMMON,
638110fa1dSEduardo Habkost                          TYPE_IVSHMEM_COMMON)
645400c02bSMarkus Armbruster 
655400c02bSMarkus Armbruster #define TYPE_IVSHMEM_PLAIN "ivshmem-plain"
668110fa1dSEduardo Habkost DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM_PLAIN,
678110fa1dSEduardo Habkost                          TYPE_IVSHMEM_PLAIN)
685400c02bSMarkus Armbruster 
695400c02bSMarkus Armbruster #define TYPE_IVSHMEM_DOORBELL "ivshmem-doorbell"
708110fa1dSEduardo Habkost DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM_DOORBELL,
718110fa1dSEduardo Habkost                          TYPE_IVSHMEM_DOORBELL)
725400c02bSMarkus Armbruster 
73eb3fedf3SPeter Crosthwaite #define TYPE_IVSHMEM "ivshmem"
748110fa1dSEduardo Habkost DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM,
758110fa1dSEduardo Habkost                          TYPE_IVSHMEM)
76eb3fedf3SPeter Crosthwaite 
776cbf4c8cSCam Macdonell typedef struct Peer {
786cbf4c8cSCam Macdonell     int nb_eventfds;
79563027ccSPaolo Bonzini     EventNotifier *eventfds;
806cbf4c8cSCam Macdonell } Peer;
816cbf4c8cSCam Macdonell 
820f57350eSMarc-André Lureau typedef struct MSIVector {
836cbf4c8cSCam Macdonell     PCIDevice *pdev;
84660c97eeSMarc-André Lureau     int virq;
85089fd803SLadi Prosek     bool unmasked;
860f57350eSMarc-André Lureau } MSIVector;
876cbf4c8cSCam Macdonell 
88db1015e9SEduardo Habkost struct IVShmemState {
89b7578eaaSAndreas Färber     /*< private >*/
90b7578eaaSAndreas Färber     PCIDevice parent_obj;
91b7578eaaSAndreas Färber     /*< public >*/
92b7578eaaSAndreas Färber 
93ddc85284SMarkus Armbruster     uint32_t features;
94ddc85284SMarkus Armbruster 
95ddc85284SMarkus Armbruster     /* exactly one of these two may be set */
96ddc85284SMarkus Armbruster     HostMemoryBackend *hostmem; /* with interrupts */
97becdfa00SMarc-André Lureau     CharBackend server_chr; /* without interrupts */
98ddc85284SMarkus Armbruster 
99ddc85284SMarkus Armbruster     /* registers */
1006cbf4c8cSCam Macdonell     uint32_t intrmask;
1016cbf4c8cSCam Macdonell     uint32_t intrstatus;
102ddc85284SMarkus Armbruster     int vm_id;
1036cbf4c8cSCam Macdonell 
104ddc85284SMarkus Armbruster     /* BARs */
105ddc85284SMarkus Armbruster     MemoryRegion ivshmem_mmio;  /* BAR 0 (registers) */
106c2d8019cSMarkus Armbruster     MemoryRegion *ivshmem_bar2; /* BAR 2 (shared memory) */
107c2d8019cSMarkus Armbruster     MemoryRegion server_bar2;   /* used with server_chr */
1086cbf4c8cSCam Macdonell 
109ddc85284SMarkus Armbruster     /* interrupt support */
1106cbf4c8cSCam Macdonell     Peer *peers;
111cd9953f7SMarkus Armbruster     int nb_peers;               /* space in @peers[] */
1126cbf4c8cSCam Macdonell     uint32_t vectors;
1130f57350eSMarc-André Lureau     MSIVector *msi_vectors;
114ee276391SMarkus Armbruster     uint64_t msg_buf;           /* buffer for receiving server messages */
115ee276391SMarkus Armbruster     int msg_buffered_bytes;     /* #bytes in @msg_buf */
1166cbf4c8cSCam Macdonell 
117ddc85284SMarkus Armbruster     /* migration stuff */
1182a845da7SMarkus Armbruster     OnOffAuto master;
11938e0735eSAnthony Liguori     Error *migration_blocker;
120db1015e9SEduardo Habkost };
1216cbf4c8cSCam Macdonell 
1226cbf4c8cSCam Macdonell /* registers for the Inter-VM shared memory device */
1236cbf4c8cSCam Macdonell enum ivshmem_registers {
1246cbf4c8cSCam Macdonell     INTRMASK = 0,
1256cbf4c8cSCam Macdonell     INTRSTATUS = 4,
1266cbf4c8cSCam Macdonell     IVPOSITION = 8,
1276cbf4c8cSCam Macdonell     DOORBELL = 12,
1286cbf4c8cSCam Macdonell };
1296cbf4c8cSCam Macdonell 
1306cbf4c8cSCam Macdonell static inline uint32_t ivshmem_has_feature(IVShmemState *ivs,
1316cbf4c8cSCam Macdonell                                                     unsigned int feature) {
1326cbf4c8cSCam Macdonell     return (ivs->features & (1 << feature));
1336cbf4c8cSCam Macdonell }
1346cbf4c8cSCam Macdonell 
1352a845da7SMarkus Armbruster static inline bool ivshmem_is_master(IVShmemState *s)
1362a845da7SMarkus Armbruster {
1372a845da7SMarkus Armbruster     assert(s->master != ON_OFF_AUTO_AUTO);
1382a845da7SMarkus Armbruster     return s->master == ON_OFF_AUTO_ON;
1392a845da7SMarkus Armbruster }
1402a845da7SMarkus Armbruster 
1416cbf4c8cSCam Macdonell static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
1426cbf4c8cSCam Macdonell {
1436cbf4c8cSCam Macdonell     IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
1446cbf4c8cSCam Macdonell 
1456cbf4c8cSCam Macdonell     s->intrmask = val;
1466cbf4c8cSCam Macdonell }
1476cbf4c8cSCam Macdonell 
1486cbf4c8cSCam Macdonell static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
1496cbf4c8cSCam Macdonell {
1506cbf4c8cSCam Macdonell     uint32_t ret = s->intrmask;
1516cbf4c8cSCam Macdonell 
1526cbf4c8cSCam Macdonell     IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret);
1536cbf4c8cSCam Macdonell     return ret;
1546cbf4c8cSCam Macdonell }
1556cbf4c8cSCam Macdonell 
1566cbf4c8cSCam Macdonell static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
1576cbf4c8cSCam Macdonell {
1586cbf4c8cSCam Macdonell     IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val);
1596cbf4c8cSCam Macdonell 
1606cbf4c8cSCam Macdonell     s->intrstatus = val;
1616cbf4c8cSCam Macdonell }
1626cbf4c8cSCam Macdonell 
1636cbf4c8cSCam Macdonell static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
1646cbf4c8cSCam Macdonell {
1656cbf4c8cSCam Macdonell     uint32_t ret = s->intrstatus;
1666cbf4c8cSCam Macdonell 
1676cbf4c8cSCam Macdonell     /* reading ISR clears all interrupts */
1686cbf4c8cSCam Macdonell     s->intrstatus = 0;
1696cbf4c8cSCam Macdonell     return ret;
1706cbf4c8cSCam Macdonell }
1716cbf4c8cSCam Macdonell 
172a8170e5eSAvi Kivity static void ivshmem_io_write(void *opaque, hwaddr addr,
173cb06608eSAvi Kivity                              uint64_t val, unsigned size)
1746cbf4c8cSCam Macdonell {
1756cbf4c8cSCam Macdonell     IVShmemState *s = opaque;
1766cbf4c8cSCam Macdonell 
1776cbf4c8cSCam Macdonell     uint16_t dest = val >> 16;
1786cbf4c8cSCam Macdonell     uint16_t vector = val & 0xff;
1796cbf4c8cSCam Macdonell 
1806cbf4c8cSCam Macdonell     addr &= 0xfc;
1816cbf4c8cSCam Macdonell 
182883f2c59SPhilippe Mathieu-Daudé     IVSHMEM_DPRINTF("writing to addr " HWADDR_FMT_plx "\n", addr);
1836cbf4c8cSCam Macdonell     switch (addr)
1846cbf4c8cSCam Macdonell     {
1856cbf4c8cSCam Macdonell         case INTRMASK:
1866cbf4c8cSCam Macdonell             ivshmem_IntrMask_write(s, val);
1876cbf4c8cSCam Macdonell             break;
1886cbf4c8cSCam Macdonell 
1896cbf4c8cSCam Macdonell         case INTRSTATUS:
1906cbf4c8cSCam Macdonell             ivshmem_IntrStatus_write(s, val);
1916cbf4c8cSCam Macdonell             break;
1926cbf4c8cSCam Macdonell 
1936cbf4c8cSCam Macdonell         case DOORBELL:
1946cbf4c8cSCam Macdonell             /* check that dest VM ID is reasonable */
19595c8425cSMarc-André Lureau             if (dest >= s->nb_peers) {
1966cbf4c8cSCam Macdonell                 IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest);
1976cbf4c8cSCam Macdonell                 break;
1986cbf4c8cSCam Macdonell             }
1996cbf4c8cSCam Macdonell 
2006cbf4c8cSCam Macdonell             /* check doorbell range */
2011b27d7a1SJes Sorensen             if (vector < s->peers[dest].nb_eventfds) {
202563027ccSPaolo Bonzini                 IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector);
203563027ccSPaolo Bonzini                 event_notifier_set(&s->peers[dest].eventfds[vector]);
204f59bb378SMarc-André Lureau             } else {
205f59bb378SMarc-André Lureau                 IVSHMEM_DPRINTF("Invalid destination vector %d on VM %d\n",
206f59bb378SMarc-André Lureau                                 vector, dest);
2076cbf4c8cSCam Macdonell             }
2086cbf4c8cSCam Macdonell             break;
2096cbf4c8cSCam Macdonell         default:
210883f2c59SPhilippe Mathieu-Daudé             IVSHMEM_DPRINTF("Unhandled write " HWADDR_FMT_plx "\n", addr);
2116cbf4c8cSCam Macdonell     }
2126cbf4c8cSCam Macdonell }
2136cbf4c8cSCam Macdonell 
214a8170e5eSAvi Kivity static uint64_t ivshmem_io_read(void *opaque, hwaddr addr,
215cb06608eSAvi Kivity                                 unsigned size)
2166cbf4c8cSCam Macdonell {
2176cbf4c8cSCam Macdonell 
2186cbf4c8cSCam Macdonell     IVShmemState *s = opaque;
2196cbf4c8cSCam Macdonell     uint32_t ret;
2206cbf4c8cSCam Macdonell 
2216cbf4c8cSCam Macdonell     switch (addr)
2226cbf4c8cSCam Macdonell     {
2236cbf4c8cSCam Macdonell         case INTRMASK:
2246cbf4c8cSCam Macdonell             ret = ivshmem_IntrMask_read(s);
2256cbf4c8cSCam Macdonell             break;
2266cbf4c8cSCam Macdonell 
2276cbf4c8cSCam Macdonell         case INTRSTATUS:
2286cbf4c8cSCam Macdonell             ret = ivshmem_IntrStatus_read(s);
2296cbf4c8cSCam Macdonell             break;
2306cbf4c8cSCam Macdonell 
2316cbf4c8cSCam Macdonell         case IVPOSITION:
2326cbf4c8cSCam Macdonell             ret = s->vm_id;
2336cbf4c8cSCam Macdonell             break;
2346cbf4c8cSCam Macdonell 
2356cbf4c8cSCam Macdonell         default:
236883f2c59SPhilippe Mathieu-Daudé             IVSHMEM_DPRINTF("why are we reading " HWADDR_FMT_plx "\n", addr);
2376cbf4c8cSCam Macdonell             ret = 0;
2386cbf4c8cSCam Macdonell     }
2396cbf4c8cSCam Macdonell 
2406cbf4c8cSCam Macdonell     return ret;
2416cbf4c8cSCam Macdonell }
2426cbf4c8cSCam Macdonell 
243cb06608eSAvi Kivity static const MemoryRegionOps ivshmem_mmio_ops = {
244cb06608eSAvi Kivity     .read = ivshmem_io_read,
245cb06608eSAvi Kivity     .write = ivshmem_io_write,
246ef80a708SDaniel Henrique Barboza     .endianness = DEVICE_LITTLE_ENDIAN,
247cb06608eSAvi Kivity     .impl = {
248cb06608eSAvi Kivity         .min_access_size = 4,
249cb06608eSAvi Kivity         .max_access_size = 4,
250cb06608eSAvi Kivity     },
2516cbf4c8cSCam Macdonell };
2526cbf4c8cSCam Macdonell 
2539940c323SMarc-André Lureau static void ivshmem_vector_notify(void *opaque)
2549940c323SMarc-André Lureau {
2550f57350eSMarc-André Lureau     MSIVector *entry = opaque;
2566cbf4c8cSCam Macdonell     PCIDevice *pdev = entry->pdev;
2575400c02bSMarkus Armbruster     IVShmemState *s = IVSHMEM_COMMON(pdev);
2580f57350eSMarc-André Lureau     int vector = entry - s->msi_vectors;
2599940c323SMarc-André Lureau     EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
2609940c323SMarc-André Lureau 
2619940c323SMarc-André Lureau     if (!event_notifier_test_and_clear(n)) {
2629940c323SMarc-André Lureau         return;
2639940c323SMarc-André Lureau     }
2646cbf4c8cSCam Macdonell 
265d160f3f7SMarc-André Lureau     IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector);
2669940c323SMarc-André Lureau     if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
267082751e8SMarkus Armbruster         if (msix_enabled(pdev)) {
268d160f3f7SMarc-André Lureau             msix_notify(pdev, vector);
269082751e8SMarkus Armbruster         }
2709940c323SMarc-André Lureau     } else {
2719940c323SMarc-André Lureau         ivshmem_IntrStatus_write(s, 1);
2729940c323SMarc-André Lureau     }
2736cbf4c8cSCam Macdonell }
2746cbf4c8cSCam Macdonell 
275660c97eeSMarc-André Lureau static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector,
276660c97eeSMarc-André Lureau                                  MSIMessage msg)
277660c97eeSMarc-André Lureau {
2785400c02bSMarkus Armbruster     IVShmemState *s = IVSHMEM_COMMON(dev);
279660c97eeSMarc-André Lureau     EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
280660c97eeSMarc-André Lureau     MSIVector *v = &s->msi_vectors[vector];
281660c97eeSMarc-André Lureau     int ret;
282660c97eeSMarc-André Lureau 
283660c97eeSMarc-André Lureau     IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector);
284e6a354beSLadi Prosek     if (!v->pdev) {
285e6a354beSLadi Prosek         error_report("ivshmem: vector %d route does not exist", vector);
286e6a354beSLadi Prosek         return -EINVAL;
287e6a354beSLadi Prosek     }
288089fd803SLadi Prosek     assert(!v->unmasked);
289660c97eeSMarc-André Lureau 
290660c97eeSMarc-André Lureau     ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev);
291660c97eeSMarc-André Lureau     if (ret < 0) {
292660c97eeSMarc-André Lureau         return ret;
293660c97eeSMarc-André Lureau     }
2943f1fea0fSPeter Xu     kvm_irqchip_commit_routes(kvm_state);
295660c97eeSMarc-André Lureau 
296089fd803SLadi Prosek     ret = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq);
297089fd803SLadi Prosek     if (ret < 0) {
298089fd803SLadi Prosek         return ret;
299089fd803SLadi Prosek     }
300089fd803SLadi Prosek     v->unmasked = true;
301089fd803SLadi Prosek 
302089fd803SLadi Prosek     return 0;
303660c97eeSMarc-André Lureau }
304660c97eeSMarc-André Lureau 
305660c97eeSMarc-André Lureau static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector)
306660c97eeSMarc-André Lureau {
3075400c02bSMarkus Armbruster     IVShmemState *s = IVSHMEM_COMMON(dev);
308660c97eeSMarc-André Lureau     EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
309e6a354beSLadi Prosek     MSIVector *v = &s->msi_vectors[vector];
310660c97eeSMarc-André Lureau     int ret;
311660c97eeSMarc-André Lureau 
312660c97eeSMarc-André Lureau     IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector);
313e6a354beSLadi Prosek     if (!v->pdev) {
314e6a354beSLadi Prosek         error_report("ivshmem: vector %d route does not exist", vector);
315e6a354beSLadi Prosek         return;
316e6a354beSLadi Prosek     }
317089fd803SLadi Prosek     assert(v->unmasked);
318660c97eeSMarc-André Lureau 
319e6a354beSLadi Prosek     ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, v->virq);
320089fd803SLadi Prosek     if (ret < 0) {
321660c97eeSMarc-André Lureau         error_report("remove_irqfd_notifier_gsi failed");
322089fd803SLadi Prosek         return;
323660c97eeSMarc-André Lureau     }
324089fd803SLadi Prosek     v->unmasked = false;
325660c97eeSMarc-André Lureau }
326660c97eeSMarc-André Lureau 
327660c97eeSMarc-André Lureau static void ivshmem_vector_poll(PCIDevice *dev,
328660c97eeSMarc-André Lureau                                 unsigned int vector_start,
329660c97eeSMarc-André Lureau                                 unsigned int vector_end)
330660c97eeSMarc-André Lureau {
3315400c02bSMarkus Armbruster     IVShmemState *s = IVSHMEM_COMMON(dev);
332660c97eeSMarc-André Lureau     unsigned int vector;
333660c97eeSMarc-André Lureau 
334660c97eeSMarc-André Lureau     IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end);
335660c97eeSMarc-André Lureau 
336660c97eeSMarc-André Lureau     vector_end = MIN(vector_end, s->vectors);
337660c97eeSMarc-André Lureau 
338660c97eeSMarc-André Lureau     for (vector = vector_start; vector < vector_end; vector++) {
339660c97eeSMarc-André Lureau         EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector];
340660c97eeSMarc-André Lureau 
341660c97eeSMarc-André Lureau         if (!msix_is_masked(dev, vector)) {
342660c97eeSMarc-André Lureau             continue;
343660c97eeSMarc-André Lureau         }
344660c97eeSMarc-André Lureau 
345660c97eeSMarc-André Lureau         if (event_notifier_test_and_clear(notifier)) {
346660c97eeSMarc-André Lureau             msix_set_pending(dev, vector);
347660c97eeSMarc-André Lureau         }
348660c97eeSMarc-André Lureau     }
349660c97eeSMarc-André Lureau }
350660c97eeSMarc-André Lureau 
3519940c323SMarc-André Lureau static void watch_vector_notifier(IVShmemState *s, EventNotifier *n,
3526cbf4c8cSCam Macdonell                                  int vector)
3536cbf4c8cSCam Macdonell {
354563027ccSPaolo Bonzini     int eventfd = event_notifier_get_fd(n);
3556cbf4c8cSCam Macdonell 
3563c27969bSMarkus Armbruster     assert(!s->msi_vectors[vector].pdev);
3570f57350eSMarc-André Lureau     s->msi_vectors[vector].pdev = PCI_DEVICE(s);
3586cbf4c8cSCam Macdonell 
3599940c323SMarc-André Lureau     qemu_set_fd_handler(eventfd, ivshmem_vector_notify,
3609940c323SMarc-André Lureau                         NULL, &s->msi_vectors[vector]);
3616cbf4c8cSCam Macdonell }
3626cbf4c8cSCam Macdonell 
363563027ccSPaolo Bonzini static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i)
364563027ccSPaolo Bonzini {
365563027ccSPaolo Bonzini     memory_region_add_eventfd(&s->ivshmem_mmio,
366563027ccSPaolo Bonzini                               DOORBELL,
367563027ccSPaolo Bonzini                               4,
368563027ccSPaolo Bonzini                               true,
369563027ccSPaolo Bonzini                               (posn << 16) | i,
370753d5e14SPaolo Bonzini                               &s->peers[posn].eventfds[i]);
371563027ccSPaolo Bonzini }
372563027ccSPaolo Bonzini 
373563027ccSPaolo Bonzini static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i)
374563027ccSPaolo Bonzini {
375563027ccSPaolo Bonzini     memory_region_del_eventfd(&s->ivshmem_mmio,
376563027ccSPaolo Bonzini                               DOORBELL,
377563027ccSPaolo Bonzini                               4,
378563027ccSPaolo Bonzini                               true,
379563027ccSPaolo Bonzini                               (posn << 16) | i,
380753d5e14SPaolo Bonzini                               &s->peers[posn].eventfds[i]);
381563027ccSPaolo Bonzini }
382563027ccSPaolo Bonzini 
383f456179fSMarc-André Lureau static void close_peer_eventfds(IVShmemState *s, int posn)
3846cbf4c8cSCam Macdonell {
385f456179fSMarc-André Lureau     int i, n;
3866cbf4c8cSCam Macdonell 
3879db51b4dSMarkus Armbruster     assert(posn >= 0 && posn < s->nb_peers);
388f456179fSMarc-André Lureau     n = s->peers[posn].nb_eventfds;
3896cbf4c8cSCam Macdonell 
3909db51b4dSMarkus Armbruster     if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
391b6a1f3a5SPaolo Bonzini         memory_region_transaction_begin();
392f456179fSMarc-André Lureau         for (i = 0; i < n; i++) {
393563027ccSPaolo Bonzini             ivshmem_del_eventfd(s, posn, i);
394b6a1f3a5SPaolo Bonzini         }
395b6a1f3a5SPaolo Bonzini         memory_region_transaction_commit();
3969db51b4dSMarkus Armbruster     }
3979db51b4dSMarkus Armbruster 
398f456179fSMarc-André Lureau     for (i = 0; i < n; i++) {
399563027ccSPaolo Bonzini         event_notifier_cleanup(&s->peers[posn].eventfds[i]);
4006cbf4c8cSCam Macdonell     }
4016cbf4c8cSCam Macdonell 
4027267c094SAnthony Liguori     g_free(s->peers[posn].eventfds);
4036cbf4c8cSCam Macdonell     s->peers[posn].nb_eventfds = 0;
4046cbf4c8cSCam Macdonell }
4056cbf4c8cSCam Macdonell 
406cd9953f7SMarkus Armbruster static void resize_peers(IVShmemState *s, int nb_peers)
40734bc07c5SSebastian Krahmer {
408cd9953f7SMarkus Armbruster     int old_nb_peers = s->nb_peers;
409cd9953f7SMarkus Armbruster     int i;
4106cbf4c8cSCam Macdonell 
411cd9953f7SMarkus Armbruster     assert(nb_peers > old_nb_peers);
412cd9953f7SMarkus Armbruster     IVSHMEM_DPRINTF("bumping storage to %d peers\n", nb_peers);
4136cbf4c8cSCam Macdonell 
414b21e2380SMarkus Armbruster     s->peers = g_renew(Peer, s->peers, nb_peers);
415cd9953f7SMarkus Armbruster     s->nb_peers = nb_peers;
416cd9953f7SMarkus Armbruster 
417cd9953f7SMarkus Armbruster     for (i = old_nb_peers; i < nb_peers; i++) {
418cd9953f7SMarkus Armbruster         s->peers[i].eventfds = g_new0(EventNotifier, s->vectors);
419cd9953f7SMarkus Armbruster         s->peers[i].nb_eventfds = 0;
42034bc07c5SSebastian Krahmer     }
4216cbf4c8cSCam Macdonell }
4226cbf4c8cSCam Macdonell 
4231309cf44SMarkus Armbruster static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector,
4241309cf44SMarkus Armbruster                                      Error **errp)
425660c97eeSMarc-André Lureau {
426660c97eeSMarc-André Lureau     PCIDevice *pdev = PCI_DEVICE(s);
427def4c557SLongpeng(Mike)     KVMRouteChange c;
428660c97eeSMarc-André Lureau     int ret;
429660c97eeSMarc-André Lureau 
430660c97eeSMarc-André Lureau     IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector);
4313c27969bSMarkus Armbruster     assert(!s->msi_vectors[vector].pdev);
432660c97eeSMarc-André Lureau 
433def4c557SLongpeng(Mike)     c = kvm_irqchip_begin_route_changes(kvm_state);
434def4c557SLongpeng(Mike)     ret = kvm_irqchip_add_msi_route(&c, vector, pdev);
435660c97eeSMarc-André Lureau     if (ret < 0) {
4361309cf44SMarkus Armbruster         error_setg(errp, "kvm_irqchip_add_msi_route failed");
4371309cf44SMarkus Armbruster         return;
438660c97eeSMarc-André Lureau     }
439def4c557SLongpeng(Mike)     kvm_irqchip_commit_route_changes(&c);
440660c97eeSMarc-André Lureau 
441660c97eeSMarc-André Lureau     s->msi_vectors[vector].virq = ret;
442660c97eeSMarc-André Lureau     s->msi_vectors[vector].pdev = pdev;
443660c97eeSMarc-André Lureau }
444660c97eeSMarc-André Lureau 
4451309cf44SMarkus Armbruster static void setup_interrupt(IVShmemState *s, int vector, Error **errp)
446660c97eeSMarc-André Lureau {
447660c97eeSMarc-André Lureau     EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
448660c97eeSMarc-André Lureau     bool with_irqfd = kvm_msi_via_irqfd_enabled() &&
449660c97eeSMarc-André Lureau         ivshmem_has_feature(s, IVSHMEM_MSI);
450660c97eeSMarc-André Lureau     PCIDevice *pdev = PCI_DEVICE(s);
4511309cf44SMarkus Armbruster     Error *err = NULL;
452660c97eeSMarc-André Lureau 
453660c97eeSMarc-André Lureau     IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector);
454660c97eeSMarc-André Lureau 
455660c97eeSMarc-André Lureau     if (!with_irqfd) {
45697553976SMarkus Armbruster         IVSHMEM_DPRINTF("with eventfd\n");
4579940c323SMarc-André Lureau         watch_vector_notifier(s, n, vector);
458660c97eeSMarc-André Lureau     } else if (msix_enabled(pdev)) {
45997553976SMarkus Armbruster         IVSHMEM_DPRINTF("with irqfd\n");
4601309cf44SMarkus Armbruster         ivshmem_add_kvm_msi_virq(s, vector, &err);
4611309cf44SMarkus Armbruster         if (err) {
4621309cf44SMarkus Armbruster             error_propagate(errp, err);
463660c97eeSMarc-André Lureau             return;
464660c97eeSMarc-André Lureau         }
465660c97eeSMarc-André Lureau 
466660c97eeSMarc-André Lureau         if (!msix_is_masked(pdev, vector)) {
467660c97eeSMarc-André Lureau             kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL,
468660c97eeSMarc-André Lureau                                                s->msi_vectors[vector].virq);
4691309cf44SMarkus Armbruster             /* TODO handle error */
470660c97eeSMarc-André Lureau         }
471660c97eeSMarc-André Lureau     } else {
472660c97eeSMarc-André Lureau         /* it will be delayed until msix is enabled, in write_config */
47397553976SMarkus Armbruster         IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled\n");
474660c97eeSMarc-André Lureau     }
475660c97eeSMarc-André Lureau }
476660c97eeSMarc-André Lureau 
4771309cf44SMarkus Armbruster static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
4786cbf4c8cSCam Macdonell {
4798baeb22bSMarkus Armbruster     struct stat buf;
4805400c02bSMarkus Armbruster     size_t size;
4816cbf4c8cSCam Macdonell 
482c2d8019cSMarkus Armbruster     if (s->ivshmem_bar2) {
4831309cf44SMarkus Armbruster         error_setg(errp, "server sent unexpected shared memory message");
484ca0b7566SMarkus Armbruster         close(fd);
485945001a1SMarc-André Lureau         return;
486945001a1SMarc-André Lureau     }
487945001a1SMarc-André Lureau 
4888baeb22bSMarkus Armbruster     if (fstat(fd, &buf) < 0) {
4898baeb22bSMarkus Armbruster         error_setg_errno(errp, errno,
4908baeb22bSMarkus Armbruster             "can't determine size of shared memory sent by server");
4918baeb22bSMarkus Armbruster         close(fd);
4928baeb22bSMarkus Armbruster         return;
4938baeb22bSMarkus Armbruster     }
4948baeb22bSMarkus Armbruster 
4955400c02bSMarkus Armbruster     size = buf.st_size;
4965400c02bSMarkus Armbruster 
4976cbf4c8cSCam Macdonell     /* mmap the region and map into the BAR2 */
4987493bd18SPhilippe Mathieu-Daudé     if (!memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s),
4997493bd18SPhilippe Mathieu-Daudé                                         "ivshmem.bar2", size, RAM_SHARED,
5007493bd18SPhilippe Mathieu-Daudé                                         fd, 0, errp)) {
501d58d7e84SMarc-André Lureau         return;
502d58d7e84SMarc-André Lureau     }
5038381d89bSMarc-André Lureau 
504c2d8019cSMarkus Armbruster     s->ivshmem_bar2 = &s->server_bar2;
5056cbf4c8cSCam Macdonell }
5066cbf4c8cSCam Macdonell 
5071309cf44SMarkus Armbruster static void process_msg_disconnect(IVShmemState *s, uint16_t posn,
5081309cf44SMarkus Armbruster                                    Error **errp)
509ca0b7566SMarkus Armbruster {
510ca0b7566SMarkus Armbruster     IVSHMEM_DPRINTF("posn %d has gone away\n", posn);
5119db51b4dSMarkus Armbruster     if (posn >= s->nb_peers || posn == s->vm_id) {
5121309cf44SMarkus Armbruster         error_setg(errp, "invalid peer %d", posn);
5139db51b4dSMarkus Armbruster         return;
5149db51b4dSMarkus Armbruster     }
515ca0b7566SMarkus Armbruster     close_peer_eventfds(s, posn);
516ca0b7566SMarkus Armbruster }
517ca0b7566SMarkus Armbruster 
5181309cf44SMarkus Armbruster static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd,
5191309cf44SMarkus Armbruster                                 Error **errp)
520ca0b7566SMarkus Armbruster {
521ca0b7566SMarkus Armbruster     Peer *peer = &s->peers[posn];
522ca0b7566SMarkus Armbruster     int vector;
523ca0b7566SMarkus Armbruster 
524ca0b7566SMarkus Armbruster     /*
525ca0b7566SMarkus Armbruster      * The N-th connect message for this peer comes with the file
526ca0b7566SMarkus Armbruster      * descriptor for vector N-1.  Count messages to find the vector.
527ca0b7566SMarkus Armbruster      */
5281ee57de4SMarc-André Lureau     if (peer->nb_eventfds >= s->vectors) {
5291309cf44SMarkus Armbruster         error_setg(errp, "Too many eventfd received, device has %d vectors",
5301ee57de4SMarc-André Lureau                    s->vectors);
531ca0b7566SMarkus Armbruster         close(fd);
5321ee57de4SMarc-André Lureau         return;
5331ee57de4SMarc-André Lureau     }
534ca0b7566SMarkus Armbruster     vector = peer->nb_eventfds++;
5351ee57de4SMarc-André Lureau 
536ca0b7566SMarkus Armbruster     IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd);
537ca0b7566SMarkus Armbruster     event_notifier_init_fd(&peer->eventfds[vector], fd);
5384d14cb0cSMarc-André Lureau     g_unix_set_fd_nonblocking(fd, true, NULL); /* msix/irqfd poll non block */
5396cbf4c8cSCam Macdonell 
540ca0b7566SMarkus Armbruster     if (posn == s->vm_id) {
5411309cf44SMarkus Armbruster         setup_interrupt(s, vector, errp);
5421309cf44SMarkus Armbruster         /* TODO do we need to handle the error? */
5436cbf4c8cSCam Macdonell     }
5446cbf4c8cSCam Macdonell 
5456cbf4c8cSCam Macdonell     if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
546ca0b7566SMarkus Armbruster         ivshmem_add_eventfd(s, posn, vector);
5476cbf4c8cSCam Macdonell     }
5486cbf4c8cSCam Macdonell }
5496cbf4c8cSCam Macdonell 
5501309cf44SMarkus Armbruster static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp)
551ca0b7566SMarkus Armbruster {
552ca0b7566SMarkus Armbruster     IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd);
553ca0b7566SMarkus Armbruster 
554ca0b7566SMarkus Armbruster     if (msg < -1 || msg > IVSHMEM_MAX_PEERS) {
5551309cf44SMarkus Armbruster         error_setg(errp, "server sent invalid message %" PRId64, msg);
556ca0b7566SMarkus Armbruster         close(fd);
557ca0b7566SMarkus Armbruster         return;
558ca0b7566SMarkus Armbruster     }
559ca0b7566SMarkus Armbruster 
560ca0b7566SMarkus Armbruster     if (msg == -1) {
5611309cf44SMarkus Armbruster         process_msg_shmem(s, fd, errp);
562ca0b7566SMarkus Armbruster         return;
563ca0b7566SMarkus Armbruster     }
564ca0b7566SMarkus Armbruster 
565ca0b7566SMarkus Armbruster     if (msg >= s->nb_peers) {
566ca0b7566SMarkus Armbruster         resize_peers(s, msg + 1);
567ca0b7566SMarkus Armbruster     }
568ca0b7566SMarkus Armbruster 
569ca0b7566SMarkus Armbruster     if (fd >= 0) {
5701309cf44SMarkus Armbruster         process_msg_connect(s, msg, fd, errp);
571ca0b7566SMarkus Armbruster     } else {
5721309cf44SMarkus Armbruster         process_msg_disconnect(s, msg, errp);
573ca0b7566SMarkus Armbruster     }
574ca0b7566SMarkus Armbruster }
575ca0b7566SMarkus Armbruster 
576ee276391SMarkus Armbruster static int ivshmem_can_receive(void *opaque)
577ee276391SMarkus Armbruster {
578ee276391SMarkus Armbruster     IVShmemState *s = opaque;
579ee276391SMarkus Armbruster 
580ee276391SMarkus Armbruster     assert(s->msg_buffered_bytes < sizeof(s->msg_buf));
581ee276391SMarkus Armbruster     return sizeof(s->msg_buf) - s->msg_buffered_bytes;
582ee276391SMarkus Armbruster }
583ee276391SMarkus Armbruster 
584ca0b7566SMarkus Armbruster static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
585ca0b7566SMarkus Armbruster {
586ca0b7566SMarkus Armbruster     IVShmemState *s = opaque;
5871309cf44SMarkus Armbruster     Error *err = NULL;
588ca0b7566SMarkus Armbruster     int fd;
589ca0b7566SMarkus Armbruster     int64_t msg;
590ca0b7566SMarkus Armbruster 
591ee276391SMarkus Armbruster     assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf));
592ee276391SMarkus Armbruster     memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size);
593ee276391SMarkus Armbruster     s->msg_buffered_bytes += size;
594ee276391SMarkus Armbruster     if (s->msg_buffered_bytes < sizeof(s->msg_buf)) {
595ca0b7566SMarkus Armbruster         return;
596ca0b7566SMarkus Armbruster     }
597ee276391SMarkus Armbruster     msg = le64_to_cpu(s->msg_buf);
598ee276391SMarkus Armbruster     s->msg_buffered_bytes = 0;
599ca0b7566SMarkus Armbruster 
6005345fdb4SMarc-André Lureau     fd = qemu_chr_fe_get_msgfd(&s->server_chr);
601ca0b7566SMarkus Armbruster 
6021309cf44SMarkus Armbruster     process_msg(s, msg, fd, &err);
6031309cf44SMarkus Armbruster     if (err) {
6041309cf44SMarkus Armbruster         error_report_err(err);
6051309cf44SMarkus Armbruster     }
606ca0b7566SMarkus Armbruster }
607ca0b7566SMarkus Armbruster 
6081309cf44SMarkus Armbruster static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, Error **errp)
6095105b1d8SDavid Marchand {
6103a55fc0fSMarkus Armbruster     int64_t msg;
6113a55fc0fSMarkus Armbruster     int n, ret;
6125105b1d8SDavid Marchand 
6133a55fc0fSMarkus Armbruster     n = 0;
6143a55fc0fSMarkus Armbruster     do {
6155345fdb4SMarc-André Lureau         ret = qemu_chr_fe_read_all(&s->server_chr, (uint8_t *)&msg + n,
6163a55fc0fSMarkus Armbruster                                    sizeof(msg) - n);
617b7b1e9ddSPhilippe Mathieu-Daudé         if (ret < 0) {
618b7b1e9ddSPhilippe Mathieu-Daudé             if (ret == -EINTR) {
619b7b1e9ddSPhilippe Mathieu-Daudé                 continue;
620b7b1e9ddSPhilippe Mathieu-Daudé             }
6211309cf44SMarkus Armbruster             error_setg_errno(errp, -ret, "read from server failed");
6223a55fc0fSMarkus Armbruster             return INT64_MIN;
6233a55fc0fSMarkus Armbruster         }
6243a55fc0fSMarkus Armbruster         n += ret;
6253a55fc0fSMarkus Armbruster     } while (n < sizeof(msg));
6263a55fc0fSMarkus Armbruster 
6275345fdb4SMarc-André Lureau     *pfd = qemu_chr_fe_get_msgfd(&s->server_chr);
62851af0ec9SThomas Huth     return le64_to_cpu(msg);
6295105b1d8SDavid Marchand }
6305105b1d8SDavid Marchand 
6311309cf44SMarkus Armbruster static void ivshmem_recv_setup(IVShmemState *s, Error **errp)
6323a55fc0fSMarkus Armbruster {
6331309cf44SMarkus Armbruster     Error *err = NULL;
6343a55fc0fSMarkus Armbruster     int64_t msg;
6353a55fc0fSMarkus Armbruster     int fd;
6363a55fc0fSMarkus Armbruster 
6371309cf44SMarkus Armbruster     msg = ivshmem_recv_msg(s, &fd, &err);
6381309cf44SMarkus Armbruster     if (err) {
6391309cf44SMarkus Armbruster         error_propagate(errp, err);
6401309cf44SMarkus Armbruster         return;
6411309cf44SMarkus Armbruster     }
6421309cf44SMarkus Armbruster     if (msg != IVSHMEM_PROTOCOL_VERSION) {
6431309cf44SMarkus Armbruster         error_setg(errp, "server sent version %" PRId64 ", expecting %d",
6441309cf44SMarkus Armbruster                    msg, IVSHMEM_PROTOCOL_VERSION);
6451309cf44SMarkus Armbruster         return;
6461309cf44SMarkus Armbruster     }
6471309cf44SMarkus Armbruster     if (fd != -1) {
6481309cf44SMarkus Armbruster         error_setg(errp, "server sent invalid version message");
6495105b1d8SDavid Marchand         return;
6505105b1d8SDavid Marchand     }
6515105b1d8SDavid Marchand 
6523a55fc0fSMarkus Armbruster     /*
653a3feb086SMarkus Armbruster      * ivshmem-server sends the remaining initial messages in a fixed
654a3feb086SMarkus Armbruster      * order, but the device has always accepted them in any order.
655a3feb086SMarkus Armbruster      * Stay as compatible as practical, just in case people use
656a3feb086SMarkus Armbruster      * servers that behave differently.
657a3feb086SMarkus Armbruster      */
658a3feb086SMarkus Armbruster 
659a3feb086SMarkus Armbruster     /*
660a3feb086SMarkus Armbruster      * ivshmem_device_spec.txt has always required the ID message
661a3feb086SMarkus Armbruster      * right here, and ivshmem-server has always complied.  However,
662a3feb086SMarkus Armbruster      * older versions of the device accepted it out of order, but
663a3feb086SMarkus Armbruster      * broke when an interrupt setup message arrived before it.
664a3feb086SMarkus Armbruster      */
665a3feb086SMarkus Armbruster     msg = ivshmem_recv_msg(s, &fd, &err);
666a3feb086SMarkus Armbruster     if (err) {
667a3feb086SMarkus Armbruster         error_propagate(errp, err);
668a3feb086SMarkus Armbruster         return;
669a3feb086SMarkus Armbruster     }
670a3feb086SMarkus Armbruster     if (fd != -1 || msg < 0 || msg > IVSHMEM_MAX_PEERS) {
671a3feb086SMarkus Armbruster         error_setg(errp, "server sent invalid ID message");
672a3feb086SMarkus Armbruster         return;
673a3feb086SMarkus Armbruster     }
674a3feb086SMarkus Armbruster     s->vm_id = msg;
675a3feb086SMarkus Armbruster 
676a3feb086SMarkus Armbruster     /*
6773a55fc0fSMarkus Armbruster      * Receive more messages until we got shared memory.
6783a55fc0fSMarkus Armbruster      */
6793a55fc0fSMarkus Armbruster     do {
6801309cf44SMarkus Armbruster         msg = ivshmem_recv_msg(s, &fd, &err);
6811309cf44SMarkus Armbruster         if (err) {
6821309cf44SMarkus Armbruster             error_propagate(errp, err);
6831309cf44SMarkus Armbruster             return;
6841309cf44SMarkus Armbruster         }
6851309cf44SMarkus Armbruster         process_msg(s, msg, fd, &err);
6861309cf44SMarkus Armbruster         if (err) {
6871309cf44SMarkus Armbruster             error_propagate(errp, err);
6881309cf44SMarkus Armbruster             return;
6891309cf44SMarkus Armbruster         }
6903a55fc0fSMarkus Armbruster     } while (msg != -1);
6911309cf44SMarkus Armbruster 
6921309cf44SMarkus Armbruster     /*
6931309cf44SMarkus Armbruster      * This function must either map the shared memory or fail.  The
6941309cf44SMarkus Armbruster      * loop above ensures that: it terminates normally only after it
6951309cf44SMarkus Armbruster      * successfully processed the server's shared memory message.
6961309cf44SMarkus Armbruster      * Assert that actually mapped the shared memory:
6971309cf44SMarkus Armbruster      */
698c2d8019cSMarkus Armbruster     assert(s->ivshmem_bar2);
6995105b1d8SDavid Marchand }
7005105b1d8SDavid Marchand 
7014490c711SMichael S. Tsirkin /* Select the MSI-X vectors used by device.
7024490c711SMichael S. Tsirkin  * ivshmem maps events to vectors statically, so
7034490c711SMichael S. Tsirkin  * we just enable all vectors on init and after reset. */
704082751e8SMarkus Armbruster static void ivshmem_msix_vector_use(IVShmemState *s)
7054490c711SMichael S. Tsirkin {
706b7578eaaSAndreas Färber     PCIDevice *d = PCI_DEVICE(s);
7074490c711SMichael S. Tsirkin     int i;
7084490c711SMichael S. Tsirkin 
7094490c711SMichael S. Tsirkin     for (i = 0; i < s->vectors; i++) {
710b7578eaaSAndreas Färber         msix_vector_use(d, i);
7114490c711SMichael S. Tsirkin     }
7124490c711SMichael S. Tsirkin }
7134490c711SMichael S. Tsirkin 
714a4022791SLadi Prosek static void ivshmem_disable_irqfd(IVShmemState *s);
715a4022791SLadi Prosek 
7166cbf4c8cSCam Macdonell static void ivshmem_reset(DeviceState *d)
7176cbf4c8cSCam Macdonell {
7185400c02bSMarkus Armbruster     IVShmemState *s = IVSHMEM_COMMON(d);
7196cbf4c8cSCam Macdonell 
720a4022791SLadi Prosek     ivshmem_disable_irqfd(s);
721a4022791SLadi Prosek 
7226cbf4c8cSCam Macdonell     s->intrstatus = 0;
723972ad215SMarc-André Lureau     s->intrmask = 0;
724082751e8SMarkus Armbruster     if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
725082751e8SMarkus Armbruster         ivshmem_msix_vector_use(s);
726082751e8SMarkus Armbruster     }
7276cbf4c8cSCam Macdonell }
7286cbf4c8cSCam Macdonell 
729ee640c62SCao jin static int ivshmem_setup_interrupts(IVShmemState *s, Error **errp)
7304490c711SMichael S. Tsirkin {
731fd47bfe5SMarc-André Lureau     /* allocate QEMU callback data for receiving interrupts */
732b21e2380SMarkus Armbruster     s->msi_vectors = g_new0(MSIVector, s->vectors);
733fd47bfe5SMarc-André Lureau 
734fd47bfe5SMarc-André Lureau     if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
735ee640c62SCao jin         if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1, errp)) {
736d58d7e84SMarc-André Lureau             return -1;
7376cbf4c8cSCam Macdonell         }
7386cbf4c8cSCam Macdonell 
7391116b539SAlex Williamson         IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors);
740082751e8SMarkus Armbruster         ivshmem_msix_vector_use(s);
741fd47bfe5SMarc-André Lureau     }
742fd47bfe5SMarc-André Lureau 
743d58d7e84SMarc-André Lureau     return 0;
7446cbf4c8cSCam Macdonell }
7456cbf4c8cSCam Macdonell 
746660c97eeSMarc-André Lureau static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector)
747660c97eeSMarc-André Lureau {
748660c97eeSMarc-André Lureau     IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector);
749660c97eeSMarc-André Lureau 
750660c97eeSMarc-André Lureau     if (s->msi_vectors[vector].pdev == NULL) {
751660c97eeSMarc-André Lureau         return;
752660c97eeSMarc-André Lureau     }
753660c97eeSMarc-André Lureau 
754660c97eeSMarc-André Lureau     /* it was cleaned when masked in the frontend. */
755660c97eeSMarc-André Lureau     kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq);
756660c97eeSMarc-André Lureau 
757660c97eeSMarc-André Lureau     s->msi_vectors[vector].pdev = NULL;
758660c97eeSMarc-André Lureau }
759660c97eeSMarc-André Lureau 
7600b88dd94SLadi Prosek static void ivshmem_enable_irqfd(IVShmemState *s)
7610b88dd94SLadi Prosek {
7620b88dd94SLadi Prosek     PCIDevice *pdev = PCI_DEVICE(s);
7630b88dd94SLadi Prosek     int i;
7640b88dd94SLadi Prosek 
7650b88dd94SLadi Prosek     for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
7660b88dd94SLadi Prosek         Error *err = NULL;
7670b88dd94SLadi Prosek 
7680b88dd94SLadi Prosek         ivshmem_add_kvm_msi_virq(s, i, &err);
7690b88dd94SLadi Prosek         if (err) {
7700b88dd94SLadi Prosek             error_report_err(err);
7710b88dd94SLadi Prosek             goto undo;
7720b88dd94SLadi Prosek         }
7730b88dd94SLadi Prosek     }
7740b88dd94SLadi Prosek 
7750b88dd94SLadi Prosek     if (msix_set_vector_notifiers(pdev,
7760b88dd94SLadi Prosek                                   ivshmem_vector_unmask,
7770b88dd94SLadi Prosek                                   ivshmem_vector_mask,
7780b88dd94SLadi Prosek                                   ivshmem_vector_poll)) {
7790b88dd94SLadi Prosek         error_report("ivshmem: msix_set_vector_notifiers failed");
7800b88dd94SLadi Prosek         goto undo;
7810b88dd94SLadi Prosek     }
7820b88dd94SLadi Prosek     return;
7830b88dd94SLadi Prosek 
7840b88dd94SLadi Prosek undo:
7850b88dd94SLadi Prosek     while (--i >= 0) {
7860b88dd94SLadi Prosek         ivshmem_remove_kvm_msi_virq(s, i);
7870b88dd94SLadi Prosek     }
7880b88dd94SLadi Prosek }
7890b88dd94SLadi Prosek 
790660c97eeSMarc-André Lureau static void ivshmem_disable_irqfd(IVShmemState *s)
791660c97eeSMarc-André Lureau {
792660c97eeSMarc-André Lureau     PCIDevice *pdev = PCI_DEVICE(s);
793660c97eeSMarc-André Lureau     int i;
794660c97eeSMarc-André Lureau 
7950b88dd94SLadi Prosek     if (!pdev->msix_vector_use_notifier) {
7960b88dd94SLadi Prosek         return;
7970b88dd94SLadi Prosek     }
7980b88dd94SLadi Prosek 
799089fd803SLadi Prosek     msix_unset_vector_notifiers(pdev);
800089fd803SLadi Prosek 
801660c97eeSMarc-André Lureau     for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
802089fd803SLadi Prosek         /*
803089fd803SLadi Prosek          * MSI-X is already disabled here so msix_unset_vector_notifiers()
804089fd803SLadi Prosek          * didn't call our release notifier.  Do it now to keep our masks and
805089fd803SLadi Prosek          * unmasks balanced.
806089fd803SLadi Prosek          */
807089fd803SLadi Prosek         if (s->msi_vectors[i].unmasked) {
808089fd803SLadi Prosek             ivshmem_vector_mask(pdev, i);
809089fd803SLadi Prosek         }
810660c97eeSMarc-André Lureau         ivshmem_remove_kvm_msi_virq(s, i);
811660c97eeSMarc-André Lureau     }
812660c97eeSMarc-André Lureau 
813660c97eeSMarc-André Lureau }
814660c97eeSMarc-André Lureau 
815660c97eeSMarc-André Lureau static void ivshmem_write_config(PCIDevice *pdev, uint32_t address,
8164490c711SMichael S. Tsirkin                                  uint32_t val, int len)
8174490c711SMichael S. Tsirkin {
8185400c02bSMarkus Armbruster     IVShmemState *s = IVSHMEM_COMMON(pdev);
819660c97eeSMarc-André Lureau     int is_enabled, was_enabled = msix_enabled(pdev);
820660c97eeSMarc-André Lureau 
821660c97eeSMarc-André Lureau     pci_default_write_config(pdev, address, val, len);
822660c97eeSMarc-André Lureau     is_enabled = msix_enabled(pdev);
823660c97eeSMarc-André Lureau 
8241309cf44SMarkus Armbruster     if (kvm_msi_via_irqfd_enabled()) {
825660c97eeSMarc-André Lureau         if (!was_enabled && is_enabled) {
826660c97eeSMarc-André Lureau             ivshmem_enable_irqfd(s);
827660c97eeSMarc-André Lureau         } else if (was_enabled && !is_enabled) {
828660c97eeSMarc-André Lureau             ivshmem_disable_irqfd(s);
829660c97eeSMarc-André Lureau         }
830660c97eeSMarc-André Lureau     }
8314490c711SMichael S. Tsirkin }
8324490c711SMichael S. Tsirkin 
8335400c02bSMarkus Armbruster static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
8346cbf4c8cSCam Macdonell {
835b691b250SZhao Liu     ERRP_GUARD();
8365400c02bSMarkus Armbruster     IVShmemState *s = IVSHMEM_COMMON(dev);
837d855e275SMarkus Armbruster     Error *err = NULL;
8386cbf4c8cSCam Macdonell     uint8_t *pci_conf;
8396cbf4c8cSCam Macdonell 
8406cbf4c8cSCam Macdonell     /* IRQFD requires MSI */
8416cbf4c8cSCam Macdonell     if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
8426cbf4c8cSCam Macdonell         !ivshmem_has_feature(s, IVSHMEM_MSI)) {
843d58d7e84SMarc-André Lureau         error_setg(errp, "ioeventfd/irqfd requires MSI");
844d58d7e84SMarc-André Lureau         return;
8456cbf4c8cSCam Macdonell     }
8466cbf4c8cSCam Macdonell 
847b7578eaaSAndreas Färber     pci_conf = dev->config;
8486cbf4c8cSCam Macdonell     pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
8496cbf4c8cSCam Macdonell 
8503c161542SPaolo Bonzini     memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s,
851cb06608eSAvi Kivity                           "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE);
852cb06608eSAvi Kivity 
8536cbf4c8cSCam Macdonell     /* region for registers*/
854b7578eaaSAndreas Färber     pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
855cb06608eSAvi Kivity                      &s->ivshmem_mmio);
856cb06608eSAvi Kivity 
857d9453c93SMarc-André Lureau     if (s->hostmem != NULL) {
858d9453c93SMarc-André Lureau         IVSHMEM_DPRINTF("using hostmem\n");
859d9453c93SMarc-André Lureau 
8607943e97bSDavid Hildenbrand         s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem);
861b266f1d1SMarkus Armbruster         host_memory_backend_set_mapped(s->hostmem, true);
8625503e285SMarkus Armbruster     } else {
8630ec7b3e7SMarc-André Lureau         Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr);
8645345fdb4SMarc-André Lureau         assert(chr);
8656dc64780SMarc-André Lureau 
8666cbf4c8cSCam Macdonell         IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
8675345fdb4SMarc-André Lureau                         chr->filename);
8686cbf4c8cSCam Macdonell 
869f456179fSMarc-André Lureau         /* we allocate enough space for 16 peers and grow as needed */
8701300b273SMarc-André Lureau         resize_peers(s, 16);
8716cbf4c8cSCam Macdonell 
8723a55fc0fSMarkus Armbruster         /*
8733a55fc0fSMarkus Armbruster          * Receive setup messages from server synchronously.
8743a55fc0fSMarkus Armbruster          * Older versions did it asynchronously, but that creates a
8753a55fc0fSMarkus Armbruster          * number of entertaining race conditions.
8763a55fc0fSMarkus Armbruster          */
8771309cf44SMarkus Armbruster         ivshmem_recv_setup(s, &err);
8781309cf44SMarkus Armbruster         if (err) {
8791309cf44SMarkus Armbruster             error_propagate(errp, err);
8801309cf44SMarkus Armbruster             return;
8811309cf44SMarkus Armbruster         }
8821309cf44SMarkus Armbruster 
88362a830b6SMarkus Armbruster         if (s->master == ON_OFF_AUTO_ON && s->vm_id != 0) {
88462a830b6SMarkus Armbruster             error_setg(errp,
88562a830b6SMarkus Armbruster                        "master must connect to the server before any peers");
88662a830b6SMarkus Armbruster             return;
88762a830b6SMarkus Armbruster         }
88862a830b6SMarkus Armbruster 
8895345fdb4SMarc-André Lureau         qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive,
89081517ba3SAnton Nefedov                                  ivshmem_read, NULL, NULL, s, NULL, true);
8913a55fc0fSMarkus Armbruster 
892ee640c62SCao jin         if (ivshmem_setup_interrupts(s, errp) < 0) {
893ee640c62SCao jin             error_prepend(errp, "Failed to initialize interrupts: ");
8943a55fc0fSMarkus Armbruster             return;
8953a55fc0fSMarkus Armbruster         }
896d855e275SMarkus Armbruster     }
897d855e275SMarkus Armbruster 
8982a845da7SMarkus Armbruster     if (s->master == ON_OFF_AUTO_AUTO) {
8992a845da7SMarkus Armbruster         s->master = s->vm_id == 0 ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
9002a845da7SMarkus Armbruster     }
9012a845da7SMarkus Armbruster 
9022a845da7SMarkus Armbruster     if (!ivshmem_is_master(s)) {
903d855e275SMarkus Armbruster         error_setg(&s->migration_blocker,
904d855e275SMarkus Armbruster                    "Migration is disabled when using feature 'peer mode' in device 'ivshmem'");
905c8a7fc51SSteve Sistare         if (migrate_add_blocker(&s->migration_blocker, errp) < 0) {
906fe44dc91SAshijeet Acharya             return;
907d58d7e84SMarc-André Lureau         }
9086cbf4c8cSCam Macdonell     }
9096cbf4c8cSCam Macdonell 
910fe44dc91SAshijeet Acharya     vmstate_register_ram(s->ivshmem_bar2, DEVICE(s));
9115a0e75f0SThomas Huth     pci_register_bar(PCI_DEVICE(s), 2,
9125a0e75f0SThomas Huth                      PCI_BASE_ADDRESS_SPACE_MEMORY |
9135a0e75f0SThomas Huth                      PCI_BASE_ADDRESS_MEM_PREFETCH |
9145a0e75f0SThomas Huth                      PCI_BASE_ADDRESS_MEM_TYPE_64,
9155a0e75f0SThomas Huth                      s->ivshmem_bar2);
916fe44dc91SAshijeet Acharya }
917fe44dc91SAshijeet Acharya 
9185400c02bSMarkus Armbruster static void ivshmem_exit(PCIDevice *dev)
9195400c02bSMarkus Armbruster {
9205400c02bSMarkus Armbruster     IVShmemState *s = IVSHMEM_COMMON(dev);
921f64a078dSMarc-André Lureau     int i;
922f64a078dSMarc-André Lureau 
923c8a7fc51SSteve Sistare     migrate_del_blocker(&s->migration_blocker);
92438e0735eSAnthony Liguori 
925c2d8019cSMarkus Armbruster     if (memory_region_is_mapped(s->ivshmem_bar2)) {
926d9453c93SMarc-André Lureau         if (!s->hostmem) {
927c2d8019cSMarkus Armbruster             void *addr = memory_region_get_ram_ptr(s->ivshmem_bar2);
92856a571d9STetsuya Mukawa             int fd;
929f64a078dSMarc-André Lureau 
9305400c02bSMarkus Armbruster             if (munmap(addr, memory_region_size(s->ivshmem_bar2) == -1)) {
931d9453c93SMarc-André Lureau                 error_report("Failed to munmap shared memory %s",
932d9453c93SMarc-André Lureau                              strerror(errno));
933d9453c93SMarc-André Lureau             }
93456a571d9STetsuya Mukawa 
9354ff87573SPaolo Bonzini             fd = memory_region_get_fd(s->ivshmem_bar2);
93656a571d9STetsuya Mukawa             close(fd);
937d9453c93SMarc-André Lureau         }
938d9453c93SMarc-André Lureau 
939c2d8019cSMarkus Armbruster         vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev));
940f64a078dSMarc-André Lureau     }
941f64a078dSMarc-André Lureau 
942b266f1d1SMarkus Armbruster     if (s->hostmem) {
943b266f1d1SMarkus Armbruster         host_memory_backend_set_mapped(s->hostmem, false);
944b266f1d1SMarkus Armbruster     }
945b266f1d1SMarkus Armbruster 
946f64a078dSMarc-André Lureau     if (s->peers) {
947f64a078dSMarc-André Lureau         for (i = 0; i < s->nb_peers; i++) {
948f456179fSMarc-André Lureau             close_peer_eventfds(s, i);
949f64a078dSMarc-André Lureau         }
950f64a078dSMarc-André Lureau         g_free(s->peers);
951f64a078dSMarc-André Lureau     }
952f64a078dSMarc-André Lureau 
953f64a078dSMarc-André Lureau     if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
954f64a078dSMarc-André Lureau         msix_uninit_exclusive_bar(dev);
955f64a078dSMarc-André Lureau     }
956f64a078dSMarc-André Lureau 
9570f57350eSMarc-André Lureau     g_free(s->msi_vectors);
9586cbf4c8cSCam Macdonell }
9596cbf4c8cSCam Macdonell 
9601f8552dfSMarc-André Lureau static int ivshmem_pre_load(void *opaque)
9611f8552dfSMarc-André Lureau {
9621f8552dfSMarc-André Lureau     IVShmemState *s = opaque;
9631f8552dfSMarc-André Lureau 
9642a845da7SMarkus Armbruster     if (!ivshmem_is_master(s)) {
9651f8552dfSMarc-André Lureau         error_report("'peer' devices are not migratable");
9661f8552dfSMarc-André Lureau         return -EINVAL;
9671f8552dfSMarc-André Lureau     }
9681f8552dfSMarc-André Lureau 
9691f8552dfSMarc-André Lureau     return 0;
9701f8552dfSMarc-André Lureau }
9711f8552dfSMarc-André Lureau 
9721f8552dfSMarc-André Lureau static int ivshmem_post_load(void *opaque, int version_id)
9731f8552dfSMarc-André Lureau {
9741f8552dfSMarc-André Lureau     IVShmemState *s = opaque;
9751f8552dfSMarc-André Lureau 
9761f8552dfSMarc-André Lureau     if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
977082751e8SMarkus Armbruster         ivshmem_msix_vector_use(s);
9781f8552dfSMarc-André Lureau     }
9791f8552dfSMarc-André Lureau     return 0;
9801f8552dfSMarc-André Lureau }
9811f8552dfSMarc-André Lureau 
9825400c02bSMarkus Armbruster static void ivshmem_common_class_init(ObjectClass *klass, void *data)
9835400c02bSMarkus Armbruster {
9845400c02bSMarkus Armbruster     DeviceClass *dc = DEVICE_CLASS(klass);
9855400c02bSMarkus Armbruster     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
9865400c02bSMarkus Armbruster 
9875400c02bSMarkus Armbruster     k->realize = ivshmem_common_realize;
9885400c02bSMarkus Armbruster     k->exit = ivshmem_exit;
9895400c02bSMarkus Armbruster     k->config_write = ivshmem_write_config;
9905400c02bSMarkus Armbruster     k->vendor_id = PCI_VENDOR_ID_IVSHMEM;
9915400c02bSMarkus Armbruster     k->device_id = PCI_DEVICE_ID_IVSHMEM;
9925400c02bSMarkus Armbruster     k->class_id = PCI_CLASS_MEMORY_RAM;
9935400c02bSMarkus Armbruster     k->revision = 1;
994*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, ivshmem_reset);
9955400c02bSMarkus Armbruster     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
9965400c02bSMarkus Armbruster     dc->desc = "Inter-VM shared memory";
9975400c02bSMarkus Armbruster }
9985400c02bSMarkus Armbruster 
999ddc85284SMarkus Armbruster static const TypeInfo ivshmem_common_info = {
1000ddc85284SMarkus Armbruster     .name          = TYPE_IVSHMEM_COMMON,
1001ddc85284SMarkus Armbruster     .parent        = TYPE_PCI_DEVICE,
1002ddc85284SMarkus Armbruster     .instance_size = sizeof(IVShmemState),
1003ddc85284SMarkus Armbruster     .abstract      = true,
1004ddc85284SMarkus Armbruster     .class_init    = ivshmem_common_class_init,
1005fd3b02c8SEduardo Habkost     .interfaces = (InterfaceInfo[]) {
1006fd3b02c8SEduardo Habkost         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
1007fd3b02c8SEduardo Habkost         { },
1008fd3b02c8SEduardo Habkost     },
1009ddc85284SMarkus Armbruster };
101040021f08SAnthony Liguori 
10115400c02bSMarkus Armbruster static const VMStateDescription ivshmem_plain_vmsd = {
10125400c02bSMarkus Armbruster     .name = TYPE_IVSHMEM_PLAIN,
10135400c02bSMarkus Armbruster     .version_id = 0,
10145400c02bSMarkus Armbruster     .minimum_version_id = 0,
10155400c02bSMarkus Armbruster     .pre_load = ivshmem_pre_load,
10165400c02bSMarkus Armbruster     .post_load = ivshmem_post_load,
1017e4ea952fSRichard Henderson     .fields = (const VMStateField[]) {
10185400c02bSMarkus Armbruster         VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
10195400c02bSMarkus Armbruster         VMSTATE_UINT32(intrstatus, IVShmemState),
10205400c02bSMarkus Armbruster         VMSTATE_UINT32(intrmask, IVShmemState),
10215400c02bSMarkus Armbruster         VMSTATE_END_OF_LIST()
10225400c02bSMarkus Armbruster     },
10235400c02bSMarkus Armbruster };
10245400c02bSMarkus Armbruster 
10255400c02bSMarkus Armbruster static Property ivshmem_plain_properties[] = {
10265400c02bSMarkus Armbruster     DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF),
1027e9cb190aSFam Zheng     DEFINE_PROP_LINK("memdev", IVShmemState, hostmem, TYPE_MEMORY_BACKEND,
1028e9cb190aSFam Zheng                      HostMemoryBackend *),
10295400c02bSMarkus Armbruster     DEFINE_PROP_END_OF_LIST(),
10305400c02bSMarkus Armbruster };
10315400c02bSMarkus Armbruster 
10326dc64780SMarc-André Lureau static void ivshmem_plain_realize(PCIDevice *dev, Error **errp)
10336dc64780SMarc-André Lureau {
10346dc64780SMarc-André Lureau     IVShmemState *s = IVSHMEM_COMMON(dev);
10356dc64780SMarc-André Lureau 
10366dc64780SMarc-André Lureau     if (!s->hostmem) {
10376dc64780SMarc-André Lureau         error_setg(errp, "You must specify a 'memdev'");
10386dc64780SMarc-André Lureau         return;
1039e9cb190aSFam Zheng     } else if (host_memory_backend_is_mapped(s->hostmem)) {
10407a309cc9SMarkus Armbruster         error_setg(errp, "can't use already busy memdev: %s",
10417a309cc9SMarkus Armbruster                    object_get_canonical_path_component(OBJECT(s->hostmem)));
1042e9cb190aSFam Zheng         return;
10436dc64780SMarc-André Lureau     }
10446dc64780SMarc-André Lureau 
10456dc64780SMarc-André Lureau     ivshmem_common_realize(dev, errp);
10466dc64780SMarc-André Lureau }
10476dc64780SMarc-André Lureau 
10485400c02bSMarkus Armbruster static void ivshmem_plain_class_init(ObjectClass *klass, void *data)
10495400c02bSMarkus Armbruster {
10505400c02bSMarkus Armbruster     DeviceClass *dc = DEVICE_CLASS(klass);
10516dc64780SMarc-André Lureau     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
10525400c02bSMarkus Armbruster 
10536dc64780SMarc-André Lureau     k->realize = ivshmem_plain_realize;
10544f67d30bSMarc-André Lureau     device_class_set_props(dc, ivshmem_plain_properties);
10555400c02bSMarkus Armbruster     dc->vmsd = &ivshmem_plain_vmsd;
10565400c02bSMarkus Armbruster }
10575400c02bSMarkus Armbruster 
10585400c02bSMarkus Armbruster static const TypeInfo ivshmem_plain_info = {
10595400c02bSMarkus Armbruster     .name          = TYPE_IVSHMEM_PLAIN,
10605400c02bSMarkus Armbruster     .parent        = TYPE_IVSHMEM_COMMON,
10615400c02bSMarkus Armbruster     .instance_size = sizeof(IVShmemState),
10625400c02bSMarkus Armbruster     .class_init    = ivshmem_plain_class_init,
10635400c02bSMarkus Armbruster };
10645400c02bSMarkus Armbruster 
10655400c02bSMarkus Armbruster static const VMStateDescription ivshmem_doorbell_vmsd = {
10665400c02bSMarkus Armbruster     .name = TYPE_IVSHMEM_DOORBELL,
10675400c02bSMarkus Armbruster     .version_id = 0,
10685400c02bSMarkus Armbruster     .minimum_version_id = 0,
10695400c02bSMarkus Armbruster     .pre_load = ivshmem_pre_load,
10705400c02bSMarkus Armbruster     .post_load = ivshmem_post_load,
1071e4ea952fSRichard Henderson     .fields = (const VMStateField[]) {
10725400c02bSMarkus Armbruster         VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
10735400c02bSMarkus Armbruster         VMSTATE_MSIX(parent_obj, IVShmemState),
10745400c02bSMarkus Armbruster         VMSTATE_UINT32(intrstatus, IVShmemState),
10755400c02bSMarkus Armbruster         VMSTATE_UINT32(intrmask, IVShmemState),
10765400c02bSMarkus Armbruster         VMSTATE_END_OF_LIST()
10775400c02bSMarkus Armbruster     },
10785400c02bSMarkus Armbruster };
10795400c02bSMarkus Armbruster 
10805400c02bSMarkus Armbruster static Property ivshmem_doorbell_properties[] = {
10815400c02bSMarkus Armbruster     DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
10825400c02bSMarkus Armbruster     DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
10835400c02bSMarkus Armbruster     DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD,
10845400c02bSMarkus Armbruster                     true),
10855400c02bSMarkus Armbruster     DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF),
10865400c02bSMarkus Armbruster     DEFINE_PROP_END_OF_LIST(),
10875400c02bSMarkus Armbruster };
10885400c02bSMarkus Armbruster 
10895400c02bSMarkus Armbruster static void ivshmem_doorbell_init(Object *obj)
10905400c02bSMarkus Armbruster {
10915400c02bSMarkus Armbruster     IVShmemState *s = IVSHMEM_DOORBELL(obj);
10925400c02bSMarkus Armbruster 
10935400c02bSMarkus Armbruster     s->features |= (1 << IVSHMEM_MSI);
10945400c02bSMarkus Armbruster }
10955400c02bSMarkus Armbruster 
10966dc64780SMarc-André Lureau static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp)
10976dc64780SMarc-André Lureau {
10986dc64780SMarc-André Lureau     IVShmemState *s = IVSHMEM_COMMON(dev);
10996dc64780SMarc-André Lureau 
110030650701SAnton Nefedov     if (!qemu_chr_fe_backend_connected(&s->server_chr)) {
11016dc64780SMarc-André Lureau         error_setg(errp, "You must specify a 'chardev'");
11026dc64780SMarc-André Lureau         return;
11036dc64780SMarc-André Lureau     }
11046dc64780SMarc-André Lureau 
11056dc64780SMarc-André Lureau     ivshmem_common_realize(dev, errp);
11066dc64780SMarc-André Lureau }
11076dc64780SMarc-André Lureau 
11085400c02bSMarkus Armbruster static void ivshmem_doorbell_class_init(ObjectClass *klass, void *data)
11095400c02bSMarkus Armbruster {
11105400c02bSMarkus Armbruster     DeviceClass *dc = DEVICE_CLASS(klass);
11116dc64780SMarc-André Lureau     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
11125400c02bSMarkus Armbruster 
11136dc64780SMarc-André Lureau     k->realize = ivshmem_doorbell_realize;
11144f67d30bSMarc-André Lureau     device_class_set_props(dc, ivshmem_doorbell_properties);
11155400c02bSMarkus Armbruster     dc->vmsd = &ivshmem_doorbell_vmsd;
11165400c02bSMarkus Armbruster }
11175400c02bSMarkus Armbruster 
11185400c02bSMarkus Armbruster static const TypeInfo ivshmem_doorbell_info = {
11195400c02bSMarkus Armbruster     .name          = TYPE_IVSHMEM_DOORBELL,
11205400c02bSMarkus Armbruster     .parent        = TYPE_IVSHMEM_COMMON,
11215400c02bSMarkus Armbruster     .instance_size = sizeof(IVShmemState),
11225400c02bSMarkus Armbruster     .instance_init = ivshmem_doorbell_init,
11235400c02bSMarkus Armbruster     .class_init    = ivshmem_doorbell_class_init,
11245400c02bSMarkus Armbruster };
11255400c02bSMarkus Armbruster 
112683f7d43aSAndreas Färber static void ivshmem_register_types(void)
11276cbf4c8cSCam Macdonell {
11285400c02bSMarkus Armbruster     type_register_static(&ivshmem_common_info);
11295400c02bSMarkus Armbruster     type_register_static(&ivshmem_plain_info);
11305400c02bSMarkus Armbruster     type_register_static(&ivshmem_doorbell_info);
11316cbf4c8cSCam Macdonell }
11326cbf4c8cSCam Macdonell 
113383f7d43aSAndreas Färber type_init(ivshmem_register_types)
1134