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