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