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"
2932cad1ffSPhilippe Mathieu-Daudé #include "system/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"
3732cad1ffSPhilippe Mathieu-Daudé #include "system/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
ivshmem_has_feature(IVShmemState * ivs,unsigned int feature)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
ivshmem_is_master(IVShmemState * s)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
ivshmem_IntrMask_write(IVShmemState * s,uint32_t val)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
ivshmem_IntrMask_read(IVShmemState * s)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
ivshmem_IntrStatus_write(IVShmemState * s,uint32_t val)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
ivshmem_IntrStatus_read(IVShmemState * s)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
ivshmem_io_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)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
ivshmem_io_read(void * opaque,hwaddr addr,unsigned size)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
ivshmem_vector_notify(void * opaque)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
ivshmem_vector_unmask(PCIDevice * dev,unsigned vector,MSIMessage msg)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
ivshmem_vector_mask(PCIDevice * dev,unsigned vector)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
ivshmem_vector_poll(PCIDevice * dev,unsigned int vector_start,unsigned int vector_end)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
watch_vector_notifier(IVShmemState * s,EventNotifier * n,int vector)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
ivshmem_add_eventfd(IVShmemState * s,int posn,int i)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
ivshmem_del_eventfd(IVShmemState * s,int posn,int i)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
close_peer_eventfds(IVShmemState * s,int posn)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
resize_peers(IVShmemState * s,int nb_peers)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
ivshmem_add_kvm_msi_virq(IVShmemState * s,int vector,Error ** errp)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
setup_interrupt(IVShmemState * s,int vector,Error ** errp)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
process_msg_shmem(IVShmemState * s,int fd,Error ** errp)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
process_msg_disconnect(IVShmemState * s,uint16_t posn,Error ** errp)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
process_msg_connect(IVShmemState * s,uint16_t posn,int fd,Error ** errp)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
process_msg(IVShmemState * s,int64_t msg,int fd,Error ** errp)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
ivshmem_can_receive(void * opaque)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
ivshmem_read(void * opaque,const uint8_t * buf,int size)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
ivshmem_recv_msg(IVShmemState * s,int * pfd,Error ** errp)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
ivshmem_recv_setup(IVShmemState * s,Error ** errp)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. */
ivshmem_msix_vector_use(IVShmemState * s)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
ivshmem_reset(DeviceState * d)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
ivshmem_setup_interrupts(IVShmemState * s,Error ** errp)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
ivshmem_remove_kvm_msi_virq(IVShmemState * s,int vector)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
ivshmem_enable_irqfd(IVShmemState * s)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
ivshmem_disable_irqfd(IVShmemState * s)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
ivshmem_write_config(PCIDevice * pdev,uint32_t address,uint32_t val,int len)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
ivshmem_common_realize(PCIDevice * dev,Error ** errp)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
ivshmem_exit(PCIDevice * dev)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
ivshmem_pre_load(void * opaque)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
ivshmem_post_load(void * opaque,int version_id)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
ivshmem_common_class_init(ObjectClass * klass,const void * data)98212d1a768SPhilippe Mathieu-Daudé static void ivshmem_common_class_init(ObjectClass *klass, const 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;
994e3d08143SPeter 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,
1005*2cd09e47SPhilippe Mathieu-Daudé .interfaces = (const 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
102530029973SRichard Henderson static const 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 };
10305400c02bSMarkus Armbruster
ivshmem_plain_realize(PCIDevice * dev,Error ** errp)10316dc64780SMarc-André Lureau static void ivshmem_plain_realize(PCIDevice *dev, Error **errp)
10326dc64780SMarc-André Lureau {
10336dc64780SMarc-André Lureau IVShmemState *s = IVSHMEM_COMMON(dev);
10346dc64780SMarc-André Lureau
10356dc64780SMarc-André Lureau if (!s->hostmem) {
10366dc64780SMarc-André Lureau error_setg(errp, "You must specify a 'memdev'");
10376dc64780SMarc-André Lureau return;
1038e9cb190aSFam Zheng } else if (host_memory_backend_is_mapped(s->hostmem)) {
10397a309cc9SMarkus Armbruster error_setg(errp, "can't use already busy memdev: %s",
10407a309cc9SMarkus Armbruster object_get_canonical_path_component(OBJECT(s->hostmem)));
1041e9cb190aSFam Zheng return;
10426dc64780SMarc-André Lureau }
10436dc64780SMarc-André Lureau
10446dc64780SMarc-André Lureau ivshmem_common_realize(dev, errp);
10456dc64780SMarc-André Lureau }
10466dc64780SMarc-André Lureau
ivshmem_plain_class_init(ObjectClass * klass,const void * data)104712d1a768SPhilippe Mathieu-Daudé static void ivshmem_plain_class_init(ObjectClass *klass, const void *data)
10485400c02bSMarkus Armbruster {
10495400c02bSMarkus Armbruster DeviceClass *dc = DEVICE_CLASS(klass);
10506dc64780SMarc-André Lureau PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
10515400c02bSMarkus Armbruster
10526dc64780SMarc-André Lureau k->realize = ivshmem_plain_realize;
10534f67d30bSMarc-André Lureau device_class_set_props(dc, ivshmem_plain_properties);
10545400c02bSMarkus Armbruster dc->vmsd = &ivshmem_plain_vmsd;
10555400c02bSMarkus Armbruster }
10565400c02bSMarkus Armbruster
10575400c02bSMarkus Armbruster static const TypeInfo ivshmem_plain_info = {
10585400c02bSMarkus Armbruster .name = TYPE_IVSHMEM_PLAIN,
10595400c02bSMarkus Armbruster .parent = TYPE_IVSHMEM_COMMON,
10605400c02bSMarkus Armbruster .instance_size = sizeof(IVShmemState),
10615400c02bSMarkus Armbruster .class_init = ivshmem_plain_class_init,
10625400c02bSMarkus Armbruster };
10635400c02bSMarkus Armbruster
10645400c02bSMarkus Armbruster static const VMStateDescription ivshmem_doorbell_vmsd = {
10655400c02bSMarkus Armbruster .name = TYPE_IVSHMEM_DOORBELL,
10665400c02bSMarkus Armbruster .version_id = 0,
10675400c02bSMarkus Armbruster .minimum_version_id = 0,
10685400c02bSMarkus Armbruster .pre_load = ivshmem_pre_load,
10695400c02bSMarkus Armbruster .post_load = ivshmem_post_load,
1070e4ea952fSRichard Henderson .fields = (const VMStateField[]) {
10715400c02bSMarkus Armbruster VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
10725400c02bSMarkus Armbruster VMSTATE_MSIX(parent_obj, IVShmemState),
10735400c02bSMarkus Armbruster VMSTATE_UINT32(intrstatus, IVShmemState),
10745400c02bSMarkus Armbruster VMSTATE_UINT32(intrmask, IVShmemState),
10755400c02bSMarkus Armbruster VMSTATE_END_OF_LIST()
10765400c02bSMarkus Armbruster },
10775400c02bSMarkus Armbruster };
10785400c02bSMarkus Armbruster
107930029973SRichard Henderson static const Property ivshmem_doorbell_properties[] = {
10805400c02bSMarkus Armbruster DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
10815400c02bSMarkus Armbruster DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
10825400c02bSMarkus Armbruster DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD,
10835400c02bSMarkus Armbruster true),
10845400c02bSMarkus Armbruster DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF),
10855400c02bSMarkus Armbruster };
10865400c02bSMarkus Armbruster
ivshmem_doorbell_init(Object * obj)10875400c02bSMarkus Armbruster static void ivshmem_doorbell_init(Object *obj)
10885400c02bSMarkus Armbruster {
10895400c02bSMarkus Armbruster IVShmemState *s = IVSHMEM_DOORBELL(obj);
10905400c02bSMarkus Armbruster
10915400c02bSMarkus Armbruster s->features |= (1 << IVSHMEM_MSI);
10925400c02bSMarkus Armbruster }
10935400c02bSMarkus Armbruster
ivshmem_doorbell_realize(PCIDevice * dev,Error ** errp)10946dc64780SMarc-André Lureau static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp)
10956dc64780SMarc-André Lureau {
10966dc64780SMarc-André Lureau IVShmemState *s = IVSHMEM_COMMON(dev);
10976dc64780SMarc-André Lureau
109830650701SAnton Nefedov if (!qemu_chr_fe_backend_connected(&s->server_chr)) {
10996dc64780SMarc-André Lureau error_setg(errp, "You must specify a 'chardev'");
11006dc64780SMarc-André Lureau return;
11016dc64780SMarc-André Lureau }
11026dc64780SMarc-André Lureau
11036dc64780SMarc-André Lureau ivshmem_common_realize(dev, errp);
11046dc64780SMarc-André Lureau }
11056dc64780SMarc-André Lureau
ivshmem_doorbell_class_init(ObjectClass * klass,const void * data)110612d1a768SPhilippe Mathieu-Daudé static void ivshmem_doorbell_class_init(ObjectClass *klass, const void *data)
11075400c02bSMarkus Armbruster {
11085400c02bSMarkus Armbruster DeviceClass *dc = DEVICE_CLASS(klass);
11096dc64780SMarc-André Lureau PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
11105400c02bSMarkus Armbruster
11116dc64780SMarc-André Lureau k->realize = ivshmem_doorbell_realize;
11124f67d30bSMarc-André Lureau device_class_set_props(dc, ivshmem_doorbell_properties);
11135400c02bSMarkus Armbruster dc->vmsd = &ivshmem_doorbell_vmsd;
11145400c02bSMarkus Armbruster }
11155400c02bSMarkus Armbruster
11165400c02bSMarkus Armbruster static const TypeInfo ivshmem_doorbell_info = {
11175400c02bSMarkus Armbruster .name = TYPE_IVSHMEM_DOORBELL,
11185400c02bSMarkus Armbruster .parent = TYPE_IVSHMEM_COMMON,
11195400c02bSMarkus Armbruster .instance_size = sizeof(IVShmemState),
11205400c02bSMarkus Armbruster .instance_init = ivshmem_doorbell_init,
11215400c02bSMarkus Armbruster .class_init = ivshmem_doorbell_class_init,
11225400c02bSMarkus Armbruster };
11235400c02bSMarkus Armbruster
ivshmem_register_types(void)112483f7d43aSAndreas Färber static void ivshmem_register_types(void)
11256cbf4c8cSCam Macdonell {
11265400c02bSMarkus Armbruster type_register_static(&ivshmem_common_info);
11275400c02bSMarkus Armbruster type_register_static(&ivshmem_plain_info);
11285400c02bSMarkus Armbruster type_register_static(&ivshmem_doorbell_info);
11296cbf4c8cSCam Macdonell }
11306cbf4c8cSCam Macdonell
113183f7d43aSAndreas Färber type_init(ivshmem_register_types)
1132