xref: /openbmc/qemu/hw/misc/ivshmem.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
1ba25df88SPaolo Bonzini /*
2ba25df88SPaolo Bonzini  * Inter-VM Shared Memory PCI device.
3ba25df88SPaolo Bonzini  *
4ba25df88SPaolo Bonzini  * Author:
5ba25df88SPaolo Bonzini  *      Cam Macdonell <cam@cs.ualberta.ca>
6ba25df88SPaolo Bonzini  *
7ba25df88SPaolo Bonzini  * Based On: cirrus_vga.c
8ba25df88SPaolo Bonzini  *          Copyright (c) 2004 Fabrice Bellard
9ba25df88SPaolo Bonzini  *          Copyright (c) 2004 Makoto Suzuki (suzu)
10ba25df88SPaolo Bonzini  *
11ba25df88SPaolo Bonzini  *      and rtl8139.c
12ba25df88SPaolo Bonzini  *          Copyright (c) 2006 Igor Kovalenko
13ba25df88SPaolo Bonzini  *
14ba25df88SPaolo Bonzini  * This code is licensed under the GNU GPL v2.
15ba25df88SPaolo Bonzini  *
16ba25df88SPaolo Bonzini  * Contributions after 2012-01-13 are licensed under the terms of the
17ba25df88SPaolo Bonzini  * GNU GPL, version 2 or (at your option) any later version.
18ba25df88SPaolo Bonzini  */
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"
24ba25df88SPaolo 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"
28ba25df88SPaolo Bonzini #include "hw/pci/msix.h"
29ba25df88SPaolo Bonzini #include "sysemu/kvm.h"
30795c40b8SJuan Quintela #include "migration/blocker.h"
31d6454270SMarkus Armbruster #include "migration/vmstate.h"
32d49b6836SMarkus Armbruster #include "qemu/error-report.h"
33ba25df88SPaolo 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"
39ba25df88SPaolo Bonzini 
405105b1d8SDavid Marchand #include "hw/misc/ivshmem.h"
41db1015e9SEduardo Habkost #include "qom/object.h"
425105b1d8SDavid Marchand 
43ba25df88SPaolo Bonzini #define PCI_VENDOR_ID_IVSHMEM   PCI_VENDOR_ID_REDHAT_QUMRANET
44ba25df88SPaolo Bonzini #define PCI_DEVICE_ID_IVSHMEM   0x1110
45ba25df88SPaolo Bonzini 
46cd9953f7SMarkus Armbruster #define IVSHMEM_MAX_PEERS UINT16_MAX
47ba25df88SPaolo Bonzini #define IVSHMEM_IOEVENTFD   0
48ba25df88SPaolo Bonzini #define IVSHMEM_MSI     1
49ba25df88SPaolo Bonzini 
50ba25df88SPaolo Bonzini #define IVSHMEM_REG_BAR_SIZE 0x100
51ba25df88SPaolo Bonzini 
52a4fa93bfSMarkus Armbruster #define IVSHMEM_DEBUG 0
53ba25df88SPaolo Bonzini #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)
59ba25df88SPaolo Bonzini 
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 
77ba25df88SPaolo Bonzini typedef struct Peer {
78ba25df88SPaolo Bonzini     int nb_eventfds;
79ba25df88SPaolo Bonzini     EventNotifier *eventfds;
80ba25df88SPaolo Bonzini } Peer;
81ba25df88SPaolo Bonzini 
820f57350eSMarc-André Lureau typedef struct MSIVector {
83ba25df88SPaolo Bonzini     PCIDevice *pdev;
84660c97eeSMarc-André Lureau     int virq;
85089fd803SLadi Prosek     bool unmasked;
860f57350eSMarc-André Lureau } MSIVector;
87ba25df88SPaolo Bonzini 
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 */
100ba25df88SPaolo Bonzini     uint32_t intrmask;
101ba25df88SPaolo Bonzini     uint32_t intrstatus;
102ddc85284SMarkus Armbruster     int vm_id;
103ba25df88SPaolo Bonzini 
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 */
108ba25df88SPaolo Bonzini 
109ddc85284SMarkus Armbruster     /* interrupt support */
110ba25df88SPaolo Bonzini     Peer *peers;
111cd9953f7SMarkus Armbruster     int nb_peers;               /* space in @peers[] */
112ba25df88SPaolo Bonzini     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 */
116ba25df88SPaolo Bonzini 
117ddc85284SMarkus Armbruster     /* migration stuff */
1182a845da7SMarkus Armbruster     OnOffAuto master;
119ba25df88SPaolo Bonzini     Error *migration_blocker;
120db1015e9SEduardo Habkost };
121ba25df88SPaolo Bonzini 
122ba25df88SPaolo Bonzini /* registers for the Inter-VM shared memory device */
123ba25df88SPaolo Bonzini enum ivshmem_registers {
124ba25df88SPaolo Bonzini     INTRMASK = 0,
125ba25df88SPaolo Bonzini     INTRSTATUS = 4,
126ba25df88SPaolo Bonzini     IVPOSITION = 8,
127ba25df88SPaolo Bonzini     DOORBELL = 12,
128ba25df88SPaolo Bonzini };
129ba25df88SPaolo Bonzini 
ivshmem_has_feature(IVShmemState * ivs,unsigned int feature)130ba25df88SPaolo Bonzini static inline uint32_t ivshmem_has_feature(IVShmemState *ivs,
131ba25df88SPaolo Bonzini                                                     unsigned int feature) {
132ba25df88SPaolo Bonzini     return (ivs->features & (1 << feature));
133ba25df88SPaolo Bonzini }
134ba25df88SPaolo Bonzini 
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)141ba25df88SPaolo Bonzini static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
142ba25df88SPaolo Bonzini {
143ba25df88SPaolo Bonzini     IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
144ba25df88SPaolo Bonzini 
145ba25df88SPaolo Bonzini     s->intrmask = val;
146ba25df88SPaolo Bonzini }
147ba25df88SPaolo Bonzini 
ivshmem_IntrMask_read(IVShmemState * s)148ba25df88SPaolo Bonzini static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
149ba25df88SPaolo Bonzini {
150ba25df88SPaolo Bonzini     uint32_t ret = s->intrmask;
151ba25df88SPaolo Bonzini 
152ba25df88SPaolo Bonzini     IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret);
153ba25df88SPaolo Bonzini     return ret;
154ba25df88SPaolo Bonzini }
155ba25df88SPaolo Bonzini 
ivshmem_IntrStatus_write(IVShmemState * s,uint32_t val)156ba25df88SPaolo Bonzini static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
157ba25df88SPaolo Bonzini {
158ba25df88SPaolo Bonzini     IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val);
159ba25df88SPaolo Bonzini 
160ba25df88SPaolo Bonzini     s->intrstatus = val;
161ba25df88SPaolo Bonzini }
162ba25df88SPaolo Bonzini 
ivshmem_IntrStatus_read(IVShmemState * s)163ba25df88SPaolo Bonzini static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
164ba25df88SPaolo Bonzini {
165ba25df88SPaolo Bonzini     uint32_t ret = s->intrstatus;
166ba25df88SPaolo Bonzini 
167ba25df88SPaolo Bonzini     /* reading ISR clears all interrupts */
168ba25df88SPaolo Bonzini     s->intrstatus = 0;
169ba25df88SPaolo Bonzini     return ret;
170ba25df88SPaolo Bonzini }
171ba25df88SPaolo Bonzini 
ivshmem_io_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)172ba25df88SPaolo Bonzini static void ivshmem_io_write(void *opaque, hwaddr addr,
173ba25df88SPaolo Bonzini                              uint64_t val, unsigned size)
174ba25df88SPaolo Bonzini {
175ba25df88SPaolo Bonzini     IVShmemState *s = opaque;
176ba25df88SPaolo Bonzini 
177ba25df88SPaolo Bonzini     uint16_t dest = val >> 16;
178ba25df88SPaolo Bonzini     uint16_t vector = val & 0xff;
179ba25df88SPaolo Bonzini 
180ba25df88SPaolo Bonzini     addr &= 0xfc;
181ba25df88SPaolo Bonzini 
182883f2c59SPhilippe Mathieu-Daudé     IVSHMEM_DPRINTF("writing to addr " HWADDR_FMT_plx "\n", addr);
183ba25df88SPaolo Bonzini     switch (addr)
184ba25df88SPaolo Bonzini     {
185ba25df88SPaolo Bonzini         case INTRMASK:
186ba25df88SPaolo Bonzini             ivshmem_IntrMask_write(s, val);
187ba25df88SPaolo Bonzini             break;
188ba25df88SPaolo Bonzini 
189ba25df88SPaolo Bonzini         case INTRSTATUS:
190ba25df88SPaolo Bonzini             ivshmem_IntrStatus_write(s, val);
191ba25df88SPaolo Bonzini             break;
192ba25df88SPaolo Bonzini 
193ba25df88SPaolo Bonzini         case DOORBELL:
194ba25df88SPaolo Bonzini             /* check that dest VM ID is reasonable */
19595c8425cSMarc-André Lureau             if (dest >= s->nb_peers) {
196ba25df88SPaolo Bonzini                 IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest);
197ba25df88SPaolo Bonzini                 break;
198ba25df88SPaolo Bonzini             }
199ba25df88SPaolo Bonzini 
200ba25df88SPaolo Bonzini             /* check doorbell range */
201ba25df88SPaolo Bonzini             if (vector < s->peers[dest].nb_eventfds) {
202ba25df88SPaolo Bonzini                 IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector);
203ba25df88SPaolo 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);
207ba25df88SPaolo Bonzini             }
208ba25df88SPaolo Bonzini             break;
209ba25df88SPaolo Bonzini         default:
210883f2c59SPhilippe Mathieu-Daudé             IVSHMEM_DPRINTF("Unhandled write " HWADDR_FMT_plx "\n", addr);
211ba25df88SPaolo Bonzini     }
212ba25df88SPaolo Bonzini }
213ba25df88SPaolo Bonzini 
ivshmem_io_read(void * opaque,hwaddr addr,unsigned size)214ba25df88SPaolo Bonzini static uint64_t ivshmem_io_read(void *opaque, hwaddr addr,
215ba25df88SPaolo Bonzini                                 unsigned size)
216ba25df88SPaolo Bonzini {
217ba25df88SPaolo Bonzini 
218ba25df88SPaolo Bonzini     IVShmemState *s = opaque;
219ba25df88SPaolo Bonzini     uint32_t ret;
220ba25df88SPaolo Bonzini 
221ba25df88SPaolo Bonzini     switch (addr)
222ba25df88SPaolo Bonzini     {
223ba25df88SPaolo Bonzini         case INTRMASK:
224ba25df88SPaolo Bonzini             ret = ivshmem_IntrMask_read(s);
225ba25df88SPaolo Bonzini             break;
226ba25df88SPaolo Bonzini 
227ba25df88SPaolo Bonzini         case INTRSTATUS:
228ba25df88SPaolo Bonzini             ret = ivshmem_IntrStatus_read(s);
229ba25df88SPaolo Bonzini             break;
230ba25df88SPaolo Bonzini 
231ba25df88SPaolo Bonzini         case IVPOSITION:
232ba25df88SPaolo Bonzini             ret = s->vm_id;
233ba25df88SPaolo Bonzini             break;
234ba25df88SPaolo Bonzini 
235ba25df88SPaolo Bonzini         default:
236883f2c59SPhilippe Mathieu-Daudé             IVSHMEM_DPRINTF("why are we reading " HWADDR_FMT_plx "\n", addr);
237ba25df88SPaolo Bonzini             ret = 0;
238ba25df88SPaolo Bonzini     }
239ba25df88SPaolo Bonzini 
240ba25df88SPaolo Bonzini     return ret;
241ba25df88SPaolo Bonzini }
242ba25df88SPaolo Bonzini 
243ba25df88SPaolo Bonzini static const MemoryRegionOps ivshmem_mmio_ops = {
244ba25df88SPaolo Bonzini     .read = ivshmem_io_read,
245ba25df88SPaolo Bonzini     .write = ivshmem_io_write,
246ef80a708SDaniel Henrique Barboza     .endianness = DEVICE_LITTLE_ENDIAN,
247ba25df88SPaolo Bonzini     .impl = {
248ba25df88SPaolo Bonzini         .min_access_size = 4,
249ba25df88SPaolo Bonzini         .max_access_size = 4,
250ba25df88SPaolo Bonzini     },
251ba25df88SPaolo Bonzini };
252ba25df88SPaolo Bonzini 
ivshmem_vector_notify(void * opaque)2539940c323SMarc-André Lureau static void ivshmem_vector_notify(void *opaque)
2549940c323SMarc-André Lureau {
2550f57350eSMarc-André Lureau     MSIVector *entry = opaque;
256ba25df88SPaolo Bonzini     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     }
264ba25df88SPaolo Bonzini 
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     }
273ba25df88SPaolo Bonzini }
274ba25df88SPaolo Bonzini 
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,
352ba25df88SPaolo Bonzini                                  int vector)
353ba25df88SPaolo Bonzini {
354ba25df88SPaolo Bonzini     int eventfd = event_notifier_get_fd(n);
355ba25df88SPaolo Bonzini 
3563c27969bSMarkus Armbruster     assert(!s->msi_vectors[vector].pdev);
3570f57350eSMarc-André Lureau     s->msi_vectors[vector].pdev = PCI_DEVICE(s);
358ba25df88SPaolo Bonzini 
3599940c323SMarc-André Lureau     qemu_set_fd_handler(eventfd, ivshmem_vector_notify,
3609940c323SMarc-André Lureau                         NULL, &s->msi_vectors[vector]);
361ba25df88SPaolo Bonzini }
362ba25df88SPaolo Bonzini 
ivshmem_add_eventfd(IVShmemState * s,int posn,int i)363ba25df88SPaolo Bonzini static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i)
364ba25df88SPaolo Bonzini {
365ba25df88SPaolo Bonzini     memory_region_add_eventfd(&s->ivshmem_mmio,
366ba25df88SPaolo Bonzini                               DOORBELL,
367ba25df88SPaolo Bonzini                               4,
368ba25df88SPaolo Bonzini                               true,
369ba25df88SPaolo Bonzini                               (posn << 16) | i,
370ba25df88SPaolo Bonzini                               &s->peers[posn].eventfds[i]);
371ba25df88SPaolo Bonzini }
372ba25df88SPaolo Bonzini 
ivshmem_del_eventfd(IVShmemState * s,int posn,int i)373ba25df88SPaolo Bonzini static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i)
374ba25df88SPaolo Bonzini {
375ba25df88SPaolo Bonzini     memory_region_del_eventfd(&s->ivshmem_mmio,
376ba25df88SPaolo Bonzini                               DOORBELL,
377ba25df88SPaolo Bonzini                               4,
378ba25df88SPaolo Bonzini                               true,
379ba25df88SPaolo Bonzini                               (posn << 16) | i,
380ba25df88SPaolo Bonzini                               &s->peers[posn].eventfds[i]);
381ba25df88SPaolo Bonzini }
382ba25df88SPaolo Bonzini 
close_peer_eventfds(IVShmemState * s,int posn)383f456179fSMarc-André Lureau static void close_peer_eventfds(IVShmemState *s, int posn)
384ba25df88SPaolo Bonzini {
385f456179fSMarc-André Lureau     int i, n;
386ba25df88SPaolo Bonzini 
3879db51b4dSMarkus Armbruster     assert(posn >= 0 && posn < s->nb_peers);
388f456179fSMarc-André Lureau     n = s->peers[posn].nb_eventfds;
389ba25df88SPaolo Bonzini 
3909db51b4dSMarkus Armbruster     if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
391ba25df88SPaolo Bonzini         memory_region_transaction_begin();
392f456179fSMarc-André Lureau         for (i = 0; i < n; i++) {
393ba25df88SPaolo Bonzini             ivshmem_del_eventfd(s, posn, i);
394ba25df88SPaolo Bonzini         }
395ba25df88SPaolo Bonzini         memory_region_transaction_commit();
3969db51b4dSMarkus Armbruster     }
3979db51b4dSMarkus Armbruster 
398f456179fSMarc-André Lureau     for (i = 0; i < n; i++) {
399ba25df88SPaolo Bonzini         event_notifier_cleanup(&s->peers[posn].eventfds[i]);
400ba25df88SPaolo Bonzini     }
401ba25df88SPaolo Bonzini 
402ba25df88SPaolo Bonzini     g_free(s->peers[posn].eventfds);
403ba25df88SPaolo Bonzini     s->peers[posn].nb_eventfds = 0;
404ba25df88SPaolo Bonzini }
405ba25df88SPaolo Bonzini 
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;
410ba25df88SPaolo Bonzini 
411cd9953f7SMarkus Armbruster     assert(nb_peers > old_nb_peers);
412cd9953f7SMarkus Armbruster     IVSHMEM_DPRINTF("bumping storage to %d peers\n", nb_peers);
413ba25df88SPaolo Bonzini 
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     }
421ba25df88SPaolo Bonzini }
422ba25df88SPaolo Bonzini 
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)
478ba25df88SPaolo Bonzini {
4798baeb22bSMarkus Armbruster     struct stat buf;
4805400c02bSMarkus Armbruster     size_t size;
481ba25df88SPaolo Bonzini 
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 
497ba25df88SPaolo Bonzini     /* 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;
505ba25df88SPaolo Bonzini }
506ba25df88SPaolo Bonzini 
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 */
539ba25df88SPaolo Bonzini 
540ca0b7566SMarkus Armbruster     if (posn == s->vm_id) {
5411309cf44SMarkus Armbruster         setup_interrupt(s, vector, errp);
5421309cf44SMarkus Armbruster         /* TODO do we need to handle the error? */
543ba25df88SPaolo Bonzini     }
544ba25df88SPaolo Bonzini 
545ba25df88SPaolo Bonzini     if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
546ca0b7566SMarkus Armbruster         ivshmem_add_eventfd(s, posn, vector);
547ba25df88SPaolo Bonzini     }
548ba25df88SPaolo Bonzini }
549ba25df88SPaolo Bonzini 
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 
701ba25df88SPaolo Bonzini /* Select the MSI-X vectors used by device.
702ba25df88SPaolo Bonzini  * ivshmem maps events to vectors statically, so
703ba25df88SPaolo Bonzini  * 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)
705ba25df88SPaolo Bonzini {
706b7578eaaSAndreas Färber     PCIDevice *d = PCI_DEVICE(s);
707ba25df88SPaolo Bonzini     int i;
708ba25df88SPaolo Bonzini 
709ba25df88SPaolo Bonzini     for (i = 0; i < s->vectors; i++) {
710b7578eaaSAndreas Färber         msix_vector_use(d, i);
711ba25df88SPaolo Bonzini     }
712ba25df88SPaolo Bonzini }
713ba25df88SPaolo Bonzini 
714a4022791SLadi Prosek static void ivshmem_disable_irqfd(IVShmemState *s);
715a4022791SLadi Prosek 
ivshmem_reset(DeviceState * d)716ba25df88SPaolo Bonzini static void ivshmem_reset(DeviceState *d)
717ba25df88SPaolo Bonzini {
7185400c02bSMarkus Armbruster     IVShmemState *s = IVSHMEM_COMMON(d);
719ba25df88SPaolo Bonzini 
720a4022791SLadi Prosek     ivshmem_disable_irqfd(s);
721a4022791SLadi Prosek 
722ba25df88SPaolo Bonzini     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     }
727ba25df88SPaolo Bonzini }
728ba25df88SPaolo Bonzini 
ivshmem_setup_interrupts(IVShmemState * s,Error ** errp)729ee640c62SCao jin static int ivshmem_setup_interrupts(IVShmemState *s, Error **errp)
730ba25df88SPaolo Bonzini {
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;
737ba25df88SPaolo Bonzini         }
738ba25df88SPaolo Bonzini 
739ba25df88SPaolo Bonzini         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;
744ba25df88SPaolo Bonzini }
745ba25df88SPaolo Bonzini 
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,
816ba25df88SPaolo Bonzini                                  uint32_t val, int len)
817ba25df88SPaolo Bonzini {
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     }
831ba25df88SPaolo Bonzini }
832ba25df88SPaolo Bonzini 
ivshmem_common_realize(PCIDevice * dev,Error ** errp)8335400c02bSMarkus Armbruster static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
834ba25df88SPaolo Bonzini {
835b691b250SZhao Liu     ERRP_GUARD();
8365400c02bSMarkus Armbruster     IVShmemState *s = IVSHMEM_COMMON(dev);
837d855e275SMarkus Armbruster     Error *err = NULL;
838ba25df88SPaolo Bonzini     uint8_t *pci_conf;
839ba25df88SPaolo Bonzini 
840ba25df88SPaolo Bonzini     /* IRQFD requires MSI */
841ba25df88SPaolo Bonzini     if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
842ba25df88SPaolo Bonzini         !ivshmem_has_feature(s, IVSHMEM_MSI)) {
843d58d7e84SMarc-André Lureau         error_setg(errp, "ioeventfd/irqfd requires MSI");
844d58d7e84SMarc-André Lureau         return;
845ba25df88SPaolo Bonzini     }
846ba25df88SPaolo Bonzini 
847b7578eaaSAndreas Färber     pci_conf = dev->config;
848ba25df88SPaolo Bonzini     pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
849ba25df88SPaolo Bonzini 
8503c161542SPaolo Bonzini     memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s,
851ba25df88SPaolo Bonzini                           "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE);
852ba25df88SPaolo Bonzini 
853ba25df88SPaolo Bonzini     /* region for registers*/
854b7578eaaSAndreas Färber     pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
855ba25df88SPaolo Bonzini                      &s->ivshmem_mmio);
856ba25df88SPaolo Bonzini 
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 
866ba25df88SPaolo Bonzini         IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
8675345fdb4SMarc-André Lureau                         chr->filename);
868ba25df88SPaolo Bonzini 
869f456179fSMarc-André Lureau         /* we allocate enough space for 16 peers and grow as needed */
8701300b273SMarc-André Lureau         resize_peers(s, 16);
871ba25df88SPaolo Bonzini 
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         }
908ba25df88SPaolo Bonzini     }
909ba25df88SPaolo Bonzini 
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);
924ba25df88SPaolo Bonzini 
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);
958ba25df88SPaolo Bonzini }
959ba25df88SPaolo Bonzini 
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,void * data)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 };
1010ba25df88SPaolo Bonzini 
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 
ivshmem_plain_realize(PCIDevice * dev,Error ** errp)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 
ivshmem_plain_class_init(ObjectClass * klass,void * data)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 
ivshmem_doorbell_init(Object * obj)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 
ivshmem_doorbell_realize(PCIDevice * dev,Error ** errp)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 
ivshmem_doorbell_class_init(ObjectClass * klass,void * data)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 
ivshmem_register_types(void)1126ba25df88SPaolo Bonzini static void ivshmem_register_types(void)
1127ba25df88SPaolo Bonzini {
11285400c02bSMarkus Armbruster     type_register_static(&ivshmem_common_info);
11295400c02bSMarkus Armbruster     type_register_static(&ivshmem_plain_info);
11305400c02bSMarkus Armbruster     type_register_static(&ivshmem_doorbell_info);
1131ba25df88SPaolo Bonzini }
1132ba25df88SPaolo Bonzini 
1133ba25df88SPaolo Bonzini type_init(ivshmem_register_types)
1134