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 { 4798381d89bSMarc-André Lureau Error *local_err = NULL; 4808baeb22bSMarkus Armbruster struct stat buf; 4815400c02bSMarkus Armbruster size_t size; 4826cbf4c8cSCam Macdonell 483c2d8019cSMarkus Armbruster if (s->ivshmem_bar2) { 4841309cf44SMarkus Armbruster error_setg(errp, "server sent unexpected shared memory message"); 485ca0b7566SMarkus Armbruster close(fd); 486945001a1SMarc-André Lureau return; 487945001a1SMarc-André Lureau } 488945001a1SMarc-André Lureau 4898baeb22bSMarkus Armbruster if (fstat(fd, &buf) < 0) { 4908baeb22bSMarkus Armbruster error_setg_errno(errp, errno, 4918baeb22bSMarkus Armbruster "can't determine size of shared memory sent by server"); 4928baeb22bSMarkus Armbruster close(fd); 4938baeb22bSMarkus Armbruster return; 4948baeb22bSMarkus Armbruster } 4958baeb22bSMarkus Armbruster 4965400c02bSMarkus Armbruster size = buf.st_size; 4975400c02bSMarkus Armbruster 4986cbf4c8cSCam Macdonell /* mmap the region and map into the BAR2 */ 499d5015b80SDavid Hildenbrand memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s), "ivshmem.bar2", 500d5015b80SDavid Hildenbrand size, RAM_SHARED, fd, 0, &local_err); 5018381d89bSMarc-André Lureau if (local_err) { 5028381d89bSMarc-André Lureau error_propagate(errp, local_err); 503d58d7e84SMarc-André Lureau return; 504d58d7e84SMarc-André Lureau } 5058381d89bSMarc-André Lureau 506c2d8019cSMarkus Armbruster s->ivshmem_bar2 = &s->server_bar2; 5076cbf4c8cSCam Macdonell } 5086cbf4c8cSCam Macdonell 5091309cf44SMarkus Armbruster static void process_msg_disconnect(IVShmemState *s, uint16_t posn, 5101309cf44SMarkus Armbruster Error **errp) 511ca0b7566SMarkus Armbruster { 512ca0b7566SMarkus Armbruster IVSHMEM_DPRINTF("posn %d has gone away\n", posn); 5139db51b4dSMarkus Armbruster if (posn >= s->nb_peers || posn == s->vm_id) { 5141309cf44SMarkus Armbruster error_setg(errp, "invalid peer %d", posn); 5159db51b4dSMarkus Armbruster return; 5169db51b4dSMarkus Armbruster } 517ca0b7566SMarkus Armbruster close_peer_eventfds(s, posn); 518ca0b7566SMarkus Armbruster } 519ca0b7566SMarkus Armbruster 5201309cf44SMarkus Armbruster static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd, 5211309cf44SMarkus Armbruster Error **errp) 522ca0b7566SMarkus Armbruster { 523ca0b7566SMarkus Armbruster Peer *peer = &s->peers[posn]; 524ca0b7566SMarkus Armbruster int vector; 525ca0b7566SMarkus Armbruster 526ca0b7566SMarkus Armbruster /* 527ca0b7566SMarkus Armbruster * The N-th connect message for this peer comes with the file 528ca0b7566SMarkus Armbruster * descriptor for vector N-1. Count messages to find the vector. 529ca0b7566SMarkus Armbruster */ 5301ee57de4SMarc-André Lureau if (peer->nb_eventfds >= s->vectors) { 5311309cf44SMarkus Armbruster error_setg(errp, "Too many eventfd received, device has %d vectors", 5321ee57de4SMarc-André Lureau s->vectors); 533ca0b7566SMarkus Armbruster close(fd); 5341ee57de4SMarc-André Lureau return; 5351ee57de4SMarc-André Lureau } 536ca0b7566SMarkus Armbruster vector = peer->nb_eventfds++; 5371ee57de4SMarc-André Lureau 538ca0b7566SMarkus Armbruster IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd); 539ca0b7566SMarkus Armbruster event_notifier_init_fd(&peer->eventfds[vector], fd); 5404d14cb0cSMarc-André Lureau g_unix_set_fd_nonblocking(fd, true, NULL); /* msix/irqfd poll non block */ 5416cbf4c8cSCam Macdonell 542ca0b7566SMarkus Armbruster if (posn == s->vm_id) { 5431309cf44SMarkus Armbruster setup_interrupt(s, vector, errp); 5441309cf44SMarkus Armbruster /* TODO do we need to handle the error? */ 5456cbf4c8cSCam Macdonell } 5466cbf4c8cSCam Macdonell 5476cbf4c8cSCam Macdonell if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { 548ca0b7566SMarkus Armbruster ivshmem_add_eventfd(s, posn, vector); 5496cbf4c8cSCam Macdonell } 5506cbf4c8cSCam Macdonell } 5516cbf4c8cSCam Macdonell 5521309cf44SMarkus Armbruster static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp) 553ca0b7566SMarkus Armbruster { 554ca0b7566SMarkus Armbruster IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd); 555ca0b7566SMarkus Armbruster 556ca0b7566SMarkus Armbruster if (msg < -1 || msg > IVSHMEM_MAX_PEERS) { 5571309cf44SMarkus Armbruster error_setg(errp, "server sent invalid message %" PRId64, msg); 558ca0b7566SMarkus Armbruster close(fd); 559ca0b7566SMarkus Armbruster return; 560ca0b7566SMarkus Armbruster } 561ca0b7566SMarkus Armbruster 562ca0b7566SMarkus Armbruster if (msg == -1) { 5631309cf44SMarkus Armbruster process_msg_shmem(s, fd, errp); 564ca0b7566SMarkus Armbruster return; 565ca0b7566SMarkus Armbruster } 566ca0b7566SMarkus Armbruster 567ca0b7566SMarkus Armbruster if (msg >= s->nb_peers) { 568ca0b7566SMarkus Armbruster resize_peers(s, msg + 1); 569ca0b7566SMarkus Armbruster } 570ca0b7566SMarkus Armbruster 571ca0b7566SMarkus Armbruster if (fd >= 0) { 5721309cf44SMarkus Armbruster process_msg_connect(s, msg, fd, errp); 573ca0b7566SMarkus Armbruster } else { 5741309cf44SMarkus Armbruster process_msg_disconnect(s, msg, errp); 575ca0b7566SMarkus Armbruster } 576ca0b7566SMarkus Armbruster } 577ca0b7566SMarkus Armbruster 578ee276391SMarkus Armbruster static int ivshmem_can_receive(void *opaque) 579ee276391SMarkus Armbruster { 580ee276391SMarkus Armbruster IVShmemState *s = opaque; 581ee276391SMarkus Armbruster 582ee276391SMarkus Armbruster assert(s->msg_buffered_bytes < sizeof(s->msg_buf)); 583ee276391SMarkus Armbruster return sizeof(s->msg_buf) - s->msg_buffered_bytes; 584ee276391SMarkus Armbruster } 585ee276391SMarkus Armbruster 586ca0b7566SMarkus Armbruster static void ivshmem_read(void *opaque, const uint8_t *buf, int size) 587ca0b7566SMarkus Armbruster { 588ca0b7566SMarkus Armbruster IVShmemState *s = opaque; 5891309cf44SMarkus Armbruster Error *err = NULL; 590ca0b7566SMarkus Armbruster int fd; 591ca0b7566SMarkus Armbruster int64_t msg; 592ca0b7566SMarkus Armbruster 593ee276391SMarkus Armbruster assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf)); 594ee276391SMarkus Armbruster memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size); 595ee276391SMarkus Armbruster s->msg_buffered_bytes += size; 596ee276391SMarkus Armbruster if (s->msg_buffered_bytes < sizeof(s->msg_buf)) { 597ca0b7566SMarkus Armbruster return; 598ca0b7566SMarkus Armbruster } 599ee276391SMarkus Armbruster msg = le64_to_cpu(s->msg_buf); 600ee276391SMarkus Armbruster s->msg_buffered_bytes = 0; 601ca0b7566SMarkus Armbruster 6025345fdb4SMarc-André Lureau fd = qemu_chr_fe_get_msgfd(&s->server_chr); 603ca0b7566SMarkus Armbruster 6041309cf44SMarkus Armbruster process_msg(s, msg, fd, &err); 6051309cf44SMarkus Armbruster if (err) { 6061309cf44SMarkus Armbruster error_report_err(err); 6071309cf44SMarkus Armbruster } 608ca0b7566SMarkus Armbruster } 609ca0b7566SMarkus Armbruster 6101309cf44SMarkus Armbruster static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, Error **errp) 6115105b1d8SDavid Marchand { 6123a55fc0fSMarkus Armbruster int64_t msg; 6133a55fc0fSMarkus Armbruster int n, ret; 6145105b1d8SDavid Marchand 6153a55fc0fSMarkus Armbruster n = 0; 6163a55fc0fSMarkus Armbruster do { 6175345fdb4SMarc-André Lureau ret = qemu_chr_fe_read_all(&s->server_chr, (uint8_t *)&msg + n, 6183a55fc0fSMarkus Armbruster sizeof(msg) - n); 619b7b1e9ddSPhilippe Mathieu-Daudé if (ret < 0) { 620b7b1e9ddSPhilippe Mathieu-Daudé if (ret == -EINTR) { 621b7b1e9ddSPhilippe Mathieu-Daudé continue; 622b7b1e9ddSPhilippe Mathieu-Daudé } 6231309cf44SMarkus Armbruster error_setg_errno(errp, -ret, "read from server failed"); 6243a55fc0fSMarkus Armbruster return INT64_MIN; 6253a55fc0fSMarkus Armbruster } 6263a55fc0fSMarkus Armbruster n += ret; 6273a55fc0fSMarkus Armbruster } while (n < sizeof(msg)); 6283a55fc0fSMarkus Armbruster 6295345fdb4SMarc-André Lureau *pfd = qemu_chr_fe_get_msgfd(&s->server_chr); 63051af0ec9SThomas Huth return le64_to_cpu(msg); 6315105b1d8SDavid Marchand } 6325105b1d8SDavid Marchand 6331309cf44SMarkus Armbruster static void ivshmem_recv_setup(IVShmemState *s, Error **errp) 6343a55fc0fSMarkus Armbruster { 6351309cf44SMarkus Armbruster Error *err = NULL; 6363a55fc0fSMarkus Armbruster int64_t msg; 6373a55fc0fSMarkus Armbruster int fd; 6383a55fc0fSMarkus Armbruster 6391309cf44SMarkus Armbruster msg = ivshmem_recv_msg(s, &fd, &err); 6401309cf44SMarkus Armbruster if (err) { 6411309cf44SMarkus Armbruster error_propagate(errp, err); 6421309cf44SMarkus Armbruster return; 6431309cf44SMarkus Armbruster } 6441309cf44SMarkus Armbruster if (msg != IVSHMEM_PROTOCOL_VERSION) { 6451309cf44SMarkus Armbruster error_setg(errp, "server sent version %" PRId64 ", expecting %d", 6461309cf44SMarkus Armbruster msg, IVSHMEM_PROTOCOL_VERSION); 6471309cf44SMarkus Armbruster return; 6481309cf44SMarkus Armbruster } 6491309cf44SMarkus Armbruster if (fd != -1) { 6501309cf44SMarkus Armbruster error_setg(errp, "server sent invalid version message"); 6515105b1d8SDavid Marchand return; 6525105b1d8SDavid Marchand } 6535105b1d8SDavid Marchand 6543a55fc0fSMarkus Armbruster /* 655a3feb086SMarkus Armbruster * ivshmem-server sends the remaining initial messages in a fixed 656a3feb086SMarkus Armbruster * order, but the device has always accepted them in any order. 657a3feb086SMarkus Armbruster * Stay as compatible as practical, just in case people use 658a3feb086SMarkus Armbruster * servers that behave differently. 659a3feb086SMarkus Armbruster */ 660a3feb086SMarkus Armbruster 661a3feb086SMarkus Armbruster /* 662a3feb086SMarkus Armbruster * ivshmem_device_spec.txt has always required the ID message 663a3feb086SMarkus Armbruster * right here, and ivshmem-server has always complied. However, 664a3feb086SMarkus Armbruster * older versions of the device accepted it out of order, but 665a3feb086SMarkus Armbruster * broke when an interrupt setup message arrived before it. 666a3feb086SMarkus Armbruster */ 667a3feb086SMarkus Armbruster msg = ivshmem_recv_msg(s, &fd, &err); 668a3feb086SMarkus Armbruster if (err) { 669a3feb086SMarkus Armbruster error_propagate(errp, err); 670a3feb086SMarkus Armbruster return; 671a3feb086SMarkus Armbruster } 672a3feb086SMarkus Armbruster if (fd != -1 || msg < 0 || msg > IVSHMEM_MAX_PEERS) { 673a3feb086SMarkus Armbruster error_setg(errp, "server sent invalid ID message"); 674a3feb086SMarkus Armbruster return; 675a3feb086SMarkus Armbruster } 676a3feb086SMarkus Armbruster s->vm_id = msg; 677a3feb086SMarkus Armbruster 678a3feb086SMarkus Armbruster /* 6793a55fc0fSMarkus Armbruster * Receive more messages until we got shared memory. 6803a55fc0fSMarkus Armbruster */ 6813a55fc0fSMarkus Armbruster do { 6821309cf44SMarkus Armbruster msg = ivshmem_recv_msg(s, &fd, &err); 6831309cf44SMarkus Armbruster if (err) { 6841309cf44SMarkus Armbruster error_propagate(errp, err); 6851309cf44SMarkus Armbruster return; 6861309cf44SMarkus Armbruster } 6871309cf44SMarkus Armbruster process_msg(s, msg, fd, &err); 6881309cf44SMarkus Armbruster if (err) { 6891309cf44SMarkus Armbruster error_propagate(errp, err); 6901309cf44SMarkus Armbruster return; 6911309cf44SMarkus Armbruster } 6923a55fc0fSMarkus Armbruster } while (msg != -1); 6931309cf44SMarkus Armbruster 6941309cf44SMarkus Armbruster /* 6951309cf44SMarkus Armbruster * This function must either map the shared memory or fail. The 6961309cf44SMarkus Armbruster * loop above ensures that: it terminates normally only after it 6971309cf44SMarkus Armbruster * successfully processed the server's shared memory message. 6981309cf44SMarkus Armbruster * Assert that actually mapped the shared memory: 6991309cf44SMarkus Armbruster */ 700c2d8019cSMarkus Armbruster assert(s->ivshmem_bar2); 7015105b1d8SDavid Marchand } 7025105b1d8SDavid Marchand 7034490c711SMichael S. Tsirkin /* Select the MSI-X vectors used by device. 7044490c711SMichael S. Tsirkin * ivshmem maps events to vectors statically, so 7054490c711SMichael S. Tsirkin * we just enable all vectors on init and after reset. */ 706082751e8SMarkus Armbruster static void ivshmem_msix_vector_use(IVShmemState *s) 7074490c711SMichael S. Tsirkin { 708b7578eaaSAndreas Färber PCIDevice *d = PCI_DEVICE(s); 7094490c711SMichael S. Tsirkin int i; 7104490c711SMichael S. Tsirkin 7114490c711SMichael S. Tsirkin for (i = 0; i < s->vectors; i++) { 712b7578eaaSAndreas Färber msix_vector_use(d, i); 7134490c711SMichael S. Tsirkin } 7144490c711SMichael S. Tsirkin } 7154490c711SMichael S. Tsirkin 716a4022791SLadi Prosek static void ivshmem_disable_irqfd(IVShmemState *s); 717a4022791SLadi Prosek 7186cbf4c8cSCam Macdonell static void ivshmem_reset(DeviceState *d) 7196cbf4c8cSCam Macdonell { 7205400c02bSMarkus Armbruster IVShmemState *s = IVSHMEM_COMMON(d); 7216cbf4c8cSCam Macdonell 722a4022791SLadi Prosek ivshmem_disable_irqfd(s); 723a4022791SLadi Prosek 7246cbf4c8cSCam Macdonell s->intrstatus = 0; 725972ad215SMarc-André Lureau s->intrmask = 0; 726082751e8SMarkus Armbruster if (ivshmem_has_feature(s, IVSHMEM_MSI)) { 727082751e8SMarkus Armbruster ivshmem_msix_vector_use(s); 728082751e8SMarkus Armbruster } 7296cbf4c8cSCam Macdonell } 7306cbf4c8cSCam Macdonell 731ee640c62SCao jin static int ivshmem_setup_interrupts(IVShmemState *s, Error **errp) 7324490c711SMichael S. Tsirkin { 733fd47bfe5SMarc-André Lureau /* allocate QEMU callback data for receiving interrupts */ 734b21e2380SMarkus Armbruster s->msi_vectors = g_new0(MSIVector, s->vectors); 735fd47bfe5SMarc-André Lureau 736fd47bfe5SMarc-André Lureau if (ivshmem_has_feature(s, IVSHMEM_MSI)) { 737ee640c62SCao jin if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1, errp)) { 738d58d7e84SMarc-André Lureau return -1; 7396cbf4c8cSCam Macdonell } 7406cbf4c8cSCam Macdonell 7411116b539SAlex Williamson IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); 742082751e8SMarkus Armbruster ivshmem_msix_vector_use(s); 743fd47bfe5SMarc-André Lureau } 744fd47bfe5SMarc-André Lureau 745d58d7e84SMarc-André Lureau return 0; 7466cbf4c8cSCam Macdonell } 7476cbf4c8cSCam Macdonell 748660c97eeSMarc-André Lureau static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector) 749660c97eeSMarc-André Lureau { 750660c97eeSMarc-André Lureau IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector); 751660c97eeSMarc-André Lureau 752660c97eeSMarc-André Lureau if (s->msi_vectors[vector].pdev == NULL) { 753660c97eeSMarc-André Lureau return; 754660c97eeSMarc-André Lureau } 755660c97eeSMarc-André Lureau 756660c97eeSMarc-André Lureau /* it was cleaned when masked in the frontend. */ 757660c97eeSMarc-André Lureau kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq); 758660c97eeSMarc-André Lureau 759660c97eeSMarc-André Lureau s->msi_vectors[vector].pdev = NULL; 760660c97eeSMarc-André Lureau } 761660c97eeSMarc-André Lureau 7620b88dd94SLadi Prosek static void ivshmem_enable_irqfd(IVShmemState *s) 7630b88dd94SLadi Prosek { 7640b88dd94SLadi Prosek PCIDevice *pdev = PCI_DEVICE(s); 7650b88dd94SLadi Prosek int i; 7660b88dd94SLadi Prosek 7670b88dd94SLadi Prosek for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { 7680b88dd94SLadi Prosek Error *err = NULL; 7690b88dd94SLadi Prosek 7700b88dd94SLadi Prosek ivshmem_add_kvm_msi_virq(s, i, &err); 7710b88dd94SLadi Prosek if (err) { 7720b88dd94SLadi Prosek error_report_err(err); 7730b88dd94SLadi Prosek goto undo; 7740b88dd94SLadi Prosek } 7750b88dd94SLadi Prosek } 7760b88dd94SLadi Prosek 7770b88dd94SLadi Prosek if (msix_set_vector_notifiers(pdev, 7780b88dd94SLadi Prosek ivshmem_vector_unmask, 7790b88dd94SLadi Prosek ivshmem_vector_mask, 7800b88dd94SLadi Prosek ivshmem_vector_poll)) { 7810b88dd94SLadi Prosek error_report("ivshmem: msix_set_vector_notifiers failed"); 7820b88dd94SLadi Prosek goto undo; 7830b88dd94SLadi Prosek } 7840b88dd94SLadi Prosek return; 7850b88dd94SLadi Prosek 7860b88dd94SLadi Prosek undo: 7870b88dd94SLadi Prosek while (--i >= 0) { 7880b88dd94SLadi Prosek ivshmem_remove_kvm_msi_virq(s, i); 7890b88dd94SLadi Prosek } 7900b88dd94SLadi Prosek } 7910b88dd94SLadi Prosek 792660c97eeSMarc-André Lureau static void ivshmem_disable_irqfd(IVShmemState *s) 793660c97eeSMarc-André Lureau { 794660c97eeSMarc-André Lureau PCIDevice *pdev = PCI_DEVICE(s); 795660c97eeSMarc-André Lureau int i; 796660c97eeSMarc-André Lureau 7970b88dd94SLadi Prosek if (!pdev->msix_vector_use_notifier) { 7980b88dd94SLadi Prosek return; 7990b88dd94SLadi Prosek } 8000b88dd94SLadi Prosek 801089fd803SLadi Prosek msix_unset_vector_notifiers(pdev); 802089fd803SLadi Prosek 803660c97eeSMarc-André Lureau for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { 804089fd803SLadi Prosek /* 805089fd803SLadi Prosek * MSI-X is already disabled here so msix_unset_vector_notifiers() 806089fd803SLadi Prosek * didn't call our release notifier. Do it now to keep our masks and 807089fd803SLadi Prosek * unmasks balanced. 808089fd803SLadi Prosek */ 809089fd803SLadi Prosek if (s->msi_vectors[i].unmasked) { 810089fd803SLadi Prosek ivshmem_vector_mask(pdev, i); 811089fd803SLadi Prosek } 812660c97eeSMarc-André Lureau ivshmem_remove_kvm_msi_virq(s, i); 813660c97eeSMarc-André Lureau } 814660c97eeSMarc-André Lureau 815660c97eeSMarc-André Lureau } 816660c97eeSMarc-André Lureau 817660c97eeSMarc-André Lureau static void ivshmem_write_config(PCIDevice *pdev, uint32_t address, 8184490c711SMichael S. Tsirkin uint32_t val, int len) 8194490c711SMichael S. Tsirkin { 8205400c02bSMarkus Armbruster IVShmemState *s = IVSHMEM_COMMON(pdev); 821660c97eeSMarc-André Lureau int is_enabled, was_enabled = msix_enabled(pdev); 822660c97eeSMarc-André Lureau 823660c97eeSMarc-André Lureau pci_default_write_config(pdev, address, val, len); 824660c97eeSMarc-André Lureau is_enabled = msix_enabled(pdev); 825660c97eeSMarc-André Lureau 8261309cf44SMarkus Armbruster if (kvm_msi_via_irqfd_enabled()) { 827660c97eeSMarc-André Lureau if (!was_enabled && is_enabled) { 828660c97eeSMarc-André Lureau ivshmem_enable_irqfd(s); 829660c97eeSMarc-André Lureau } else if (was_enabled && !is_enabled) { 830660c97eeSMarc-André Lureau ivshmem_disable_irqfd(s); 831660c97eeSMarc-André Lureau } 832660c97eeSMarc-André Lureau } 8334490c711SMichael S. Tsirkin } 8344490c711SMichael S. Tsirkin 8355400c02bSMarkus Armbruster static void ivshmem_common_realize(PCIDevice *dev, Error **errp) 8366cbf4c8cSCam Macdonell { 8375400c02bSMarkus Armbruster IVShmemState *s = IVSHMEM_COMMON(dev); 838d855e275SMarkus Armbruster Error *err = NULL; 8396cbf4c8cSCam Macdonell uint8_t *pci_conf; 8406cbf4c8cSCam Macdonell 8416cbf4c8cSCam Macdonell /* IRQFD requires MSI */ 8426cbf4c8cSCam Macdonell if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && 8436cbf4c8cSCam Macdonell !ivshmem_has_feature(s, IVSHMEM_MSI)) { 844d58d7e84SMarc-André Lureau error_setg(errp, "ioeventfd/irqfd requires MSI"); 845d58d7e84SMarc-André Lureau return; 8466cbf4c8cSCam Macdonell } 8476cbf4c8cSCam Macdonell 848b7578eaaSAndreas Färber pci_conf = dev->config; 8496cbf4c8cSCam Macdonell pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; 8506cbf4c8cSCam Macdonell 8513c161542SPaolo Bonzini memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s, 852cb06608eSAvi Kivity "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE); 853cb06608eSAvi Kivity 8546cbf4c8cSCam Macdonell /* region for registers*/ 855b7578eaaSAndreas Färber pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, 856cb06608eSAvi Kivity &s->ivshmem_mmio); 857cb06608eSAvi Kivity 858d9453c93SMarc-André Lureau if (s->hostmem != NULL) { 859d9453c93SMarc-André Lureau IVSHMEM_DPRINTF("using hostmem\n"); 860d9453c93SMarc-André Lureau 8617943e97bSDavid Hildenbrand s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem); 862b266f1d1SMarkus Armbruster host_memory_backend_set_mapped(s->hostmem, true); 8635503e285SMarkus Armbruster } else { 8640ec7b3e7SMarc-André Lureau Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr); 8655345fdb4SMarc-André Lureau assert(chr); 8666dc64780SMarc-André Lureau 8676cbf4c8cSCam Macdonell IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", 8685345fdb4SMarc-André Lureau chr->filename); 8696cbf4c8cSCam Macdonell 870f456179fSMarc-André Lureau /* we allocate enough space for 16 peers and grow as needed */ 8711300b273SMarc-André Lureau resize_peers(s, 16); 8726cbf4c8cSCam Macdonell 8733a55fc0fSMarkus Armbruster /* 8743a55fc0fSMarkus Armbruster * Receive setup messages from server synchronously. 8753a55fc0fSMarkus Armbruster * Older versions did it asynchronously, but that creates a 8763a55fc0fSMarkus Armbruster * number of entertaining race conditions. 8773a55fc0fSMarkus Armbruster */ 8781309cf44SMarkus Armbruster ivshmem_recv_setup(s, &err); 8791309cf44SMarkus Armbruster if (err) { 8801309cf44SMarkus Armbruster error_propagate(errp, err); 8811309cf44SMarkus Armbruster return; 8821309cf44SMarkus Armbruster } 8831309cf44SMarkus Armbruster 88462a830b6SMarkus Armbruster if (s->master == ON_OFF_AUTO_ON && s->vm_id != 0) { 88562a830b6SMarkus Armbruster error_setg(errp, 88662a830b6SMarkus Armbruster "master must connect to the server before any peers"); 88762a830b6SMarkus Armbruster return; 88862a830b6SMarkus Armbruster } 88962a830b6SMarkus Armbruster 8905345fdb4SMarc-André Lureau qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive, 89181517ba3SAnton Nefedov ivshmem_read, NULL, NULL, s, NULL, true); 8923a55fc0fSMarkus Armbruster 893ee640c62SCao jin if (ivshmem_setup_interrupts(s, errp) < 0) { 894ee640c62SCao jin error_prepend(errp, "Failed to initialize interrupts: "); 8953a55fc0fSMarkus Armbruster return; 8963a55fc0fSMarkus Armbruster } 897d855e275SMarkus Armbruster } 898d855e275SMarkus Armbruster 8992a845da7SMarkus Armbruster if (s->master == ON_OFF_AUTO_AUTO) { 9002a845da7SMarkus Armbruster s->master = s->vm_id == 0 ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; 9012a845da7SMarkus Armbruster } 9022a845da7SMarkus Armbruster 9032a845da7SMarkus Armbruster if (!ivshmem_is_master(s)) { 904d855e275SMarkus Armbruster error_setg(&s->migration_blocker, 905d855e275SMarkus Armbruster "Migration is disabled when using feature 'peer mode' in device 'ivshmem'"); 906c8a7fc51SSteve Sistare if (migrate_add_blocker(&s->migration_blocker, errp) < 0) { 907fe44dc91SAshijeet Acharya return; 908d58d7e84SMarc-André Lureau } 9096cbf4c8cSCam Macdonell } 9106cbf4c8cSCam Macdonell 911fe44dc91SAshijeet Acharya vmstate_register_ram(s->ivshmem_bar2, DEVICE(s)); 9125a0e75f0SThomas Huth pci_register_bar(PCI_DEVICE(s), 2, 9135a0e75f0SThomas Huth PCI_BASE_ADDRESS_SPACE_MEMORY | 9145a0e75f0SThomas Huth PCI_BASE_ADDRESS_MEM_PREFETCH | 9155a0e75f0SThomas Huth PCI_BASE_ADDRESS_MEM_TYPE_64, 9165a0e75f0SThomas Huth s->ivshmem_bar2); 917fe44dc91SAshijeet Acharya } 918fe44dc91SAshijeet Acharya 9195400c02bSMarkus Armbruster static void ivshmem_exit(PCIDevice *dev) 9205400c02bSMarkus Armbruster { 9215400c02bSMarkus Armbruster IVShmemState *s = IVSHMEM_COMMON(dev); 922f64a078dSMarc-André Lureau int i; 923f64a078dSMarc-André Lureau 924c8a7fc51SSteve Sistare migrate_del_blocker(&s->migration_blocker); 92538e0735eSAnthony Liguori 926c2d8019cSMarkus Armbruster if (memory_region_is_mapped(s->ivshmem_bar2)) { 927d9453c93SMarc-André Lureau if (!s->hostmem) { 928c2d8019cSMarkus Armbruster void *addr = memory_region_get_ram_ptr(s->ivshmem_bar2); 92956a571d9STetsuya Mukawa int fd; 930f64a078dSMarc-André Lureau 9315400c02bSMarkus Armbruster if (munmap(addr, memory_region_size(s->ivshmem_bar2) == -1)) { 932d9453c93SMarc-André Lureau error_report("Failed to munmap shared memory %s", 933d9453c93SMarc-André Lureau strerror(errno)); 934d9453c93SMarc-André Lureau } 93556a571d9STetsuya Mukawa 9364ff87573SPaolo Bonzini fd = memory_region_get_fd(s->ivshmem_bar2); 93756a571d9STetsuya Mukawa close(fd); 938d9453c93SMarc-André Lureau } 939d9453c93SMarc-André Lureau 940c2d8019cSMarkus Armbruster vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev)); 941f64a078dSMarc-André Lureau } 942f64a078dSMarc-André Lureau 943b266f1d1SMarkus Armbruster if (s->hostmem) { 944b266f1d1SMarkus Armbruster host_memory_backend_set_mapped(s->hostmem, false); 945b266f1d1SMarkus Armbruster } 946b266f1d1SMarkus Armbruster 947f64a078dSMarc-André Lureau if (s->peers) { 948f64a078dSMarc-André Lureau for (i = 0; i < s->nb_peers; i++) { 949f456179fSMarc-André Lureau close_peer_eventfds(s, i); 950f64a078dSMarc-André Lureau } 951f64a078dSMarc-André Lureau g_free(s->peers); 952f64a078dSMarc-André Lureau } 953f64a078dSMarc-André Lureau 954f64a078dSMarc-André Lureau if (ivshmem_has_feature(s, IVSHMEM_MSI)) { 955f64a078dSMarc-André Lureau msix_uninit_exclusive_bar(dev); 956f64a078dSMarc-André Lureau } 957f64a078dSMarc-André Lureau 9580f57350eSMarc-André Lureau g_free(s->msi_vectors); 9596cbf4c8cSCam Macdonell } 9606cbf4c8cSCam Macdonell 9611f8552dfSMarc-André Lureau static int ivshmem_pre_load(void *opaque) 9621f8552dfSMarc-André Lureau { 9631f8552dfSMarc-André Lureau IVShmemState *s = opaque; 9641f8552dfSMarc-André Lureau 9652a845da7SMarkus Armbruster if (!ivshmem_is_master(s)) { 9661f8552dfSMarc-André Lureau error_report("'peer' devices are not migratable"); 9671f8552dfSMarc-André Lureau return -EINVAL; 9681f8552dfSMarc-André Lureau } 9691f8552dfSMarc-André Lureau 9701f8552dfSMarc-André Lureau return 0; 9711f8552dfSMarc-André Lureau } 9721f8552dfSMarc-André Lureau 9731f8552dfSMarc-André Lureau static int ivshmem_post_load(void *opaque, int version_id) 9741f8552dfSMarc-André Lureau { 9751f8552dfSMarc-André Lureau IVShmemState *s = opaque; 9761f8552dfSMarc-André Lureau 9771f8552dfSMarc-André Lureau if (ivshmem_has_feature(s, IVSHMEM_MSI)) { 978082751e8SMarkus Armbruster ivshmem_msix_vector_use(s); 9791f8552dfSMarc-André Lureau } 9801f8552dfSMarc-André Lureau return 0; 9811f8552dfSMarc-André Lureau } 9821f8552dfSMarc-André Lureau 9835400c02bSMarkus Armbruster static void ivshmem_common_class_init(ObjectClass *klass, void *data) 9845400c02bSMarkus Armbruster { 9855400c02bSMarkus Armbruster DeviceClass *dc = DEVICE_CLASS(klass); 9865400c02bSMarkus Armbruster PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 9875400c02bSMarkus Armbruster 9885400c02bSMarkus Armbruster k->realize = ivshmem_common_realize; 9895400c02bSMarkus Armbruster k->exit = ivshmem_exit; 9905400c02bSMarkus Armbruster k->config_write = ivshmem_write_config; 9915400c02bSMarkus Armbruster k->vendor_id = PCI_VENDOR_ID_IVSHMEM; 9925400c02bSMarkus Armbruster k->device_id = PCI_DEVICE_ID_IVSHMEM; 9935400c02bSMarkus Armbruster k->class_id = PCI_CLASS_MEMORY_RAM; 9945400c02bSMarkus Armbruster k->revision = 1; 9955400c02bSMarkus Armbruster dc->reset = ivshmem_reset; 9965400c02bSMarkus Armbruster set_bit(DEVICE_CATEGORY_MISC, dc->categories); 9975400c02bSMarkus Armbruster dc->desc = "Inter-VM shared memory"; 9985400c02bSMarkus Armbruster } 9995400c02bSMarkus Armbruster 1000ddc85284SMarkus Armbruster static const TypeInfo ivshmem_common_info = { 1001ddc85284SMarkus Armbruster .name = TYPE_IVSHMEM_COMMON, 1002ddc85284SMarkus Armbruster .parent = TYPE_PCI_DEVICE, 1003ddc85284SMarkus Armbruster .instance_size = sizeof(IVShmemState), 1004ddc85284SMarkus Armbruster .abstract = true, 1005ddc85284SMarkus Armbruster .class_init = ivshmem_common_class_init, 1006fd3b02c8SEduardo Habkost .interfaces = (InterfaceInfo[]) { 1007fd3b02c8SEduardo Habkost { INTERFACE_CONVENTIONAL_PCI_DEVICE }, 1008fd3b02c8SEduardo Habkost { }, 1009fd3b02c8SEduardo Habkost }, 1010ddc85284SMarkus Armbruster }; 101140021f08SAnthony Liguori 10125400c02bSMarkus Armbruster static const VMStateDescription ivshmem_plain_vmsd = { 10135400c02bSMarkus Armbruster .name = TYPE_IVSHMEM_PLAIN, 10145400c02bSMarkus Armbruster .version_id = 0, 10155400c02bSMarkus Armbruster .minimum_version_id = 0, 10165400c02bSMarkus Armbruster .pre_load = ivshmem_pre_load, 10175400c02bSMarkus Armbruster .post_load = ivshmem_post_load, 1018*e4ea952fSRichard Henderson .fields = (const VMStateField[]) { 10195400c02bSMarkus Armbruster VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), 10205400c02bSMarkus Armbruster VMSTATE_UINT32(intrstatus, IVShmemState), 10215400c02bSMarkus Armbruster VMSTATE_UINT32(intrmask, IVShmemState), 10225400c02bSMarkus Armbruster VMSTATE_END_OF_LIST() 10235400c02bSMarkus Armbruster }, 10245400c02bSMarkus Armbruster }; 10255400c02bSMarkus Armbruster 10265400c02bSMarkus Armbruster static Property ivshmem_plain_properties[] = { 10275400c02bSMarkus Armbruster DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), 1028e9cb190aSFam Zheng DEFINE_PROP_LINK("memdev", IVShmemState, hostmem, TYPE_MEMORY_BACKEND, 1029e9cb190aSFam Zheng HostMemoryBackend *), 10305400c02bSMarkus Armbruster DEFINE_PROP_END_OF_LIST(), 10315400c02bSMarkus Armbruster }; 10325400c02bSMarkus Armbruster 10336dc64780SMarc-André Lureau static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) 10346dc64780SMarc-André Lureau { 10356dc64780SMarc-André Lureau IVShmemState *s = IVSHMEM_COMMON(dev); 10366dc64780SMarc-André Lureau 10376dc64780SMarc-André Lureau if (!s->hostmem) { 10386dc64780SMarc-André Lureau error_setg(errp, "You must specify a 'memdev'"); 10396dc64780SMarc-André Lureau return; 1040e9cb190aSFam Zheng } else if (host_memory_backend_is_mapped(s->hostmem)) { 10417a309cc9SMarkus Armbruster error_setg(errp, "can't use already busy memdev: %s", 10427a309cc9SMarkus Armbruster object_get_canonical_path_component(OBJECT(s->hostmem))); 1043e9cb190aSFam Zheng return; 10446dc64780SMarc-André Lureau } 10456dc64780SMarc-André Lureau 10466dc64780SMarc-André Lureau ivshmem_common_realize(dev, errp); 10476dc64780SMarc-André Lureau } 10486dc64780SMarc-André Lureau 10495400c02bSMarkus Armbruster static void ivshmem_plain_class_init(ObjectClass *klass, void *data) 10505400c02bSMarkus Armbruster { 10515400c02bSMarkus Armbruster DeviceClass *dc = DEVICE_CLASS(klass); 10526dc64780SMarc-André Lureau PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 10535400c02bSMarkus Armbruster 10546dc64780SMarc-André Lureau k->realize = ivshmem_plain_realize; 10554f67d30bSMarc-André Lureau device_class_set_props(dc, ivshmem_plain_properties); 10565400c02bSMarkus Armbruster dc->vmsd = &ivshmem_plain_vmsd; 10575400c02bSMarkus Armbruster } 10585400c02bSMarkus Armbruster 10595400c02bSMarkus Armbruster static const TypeInfo ivshmem_plain_info = { 10605400c02bSMarkus Armbruster .name = TYPE_IVSHMEM_PLAIN, 10615400c02bSMarkus Armbruster .parent = TYPE_IVSHMEM_COMMON, 10625400c02bSMarkus Armbruster .instance_size = sizeof(IVShmemState), 10635400c02bSMarkus Armbruster .class_init = ivshmem_plain_class_init, 10645400c02bSMarkus Armbruster }; 10655400c02bSMarkus Armbruster 10665400c02bSMarkus Armbruster static const VMStateDescription ivshmem_doorbell_vmsd = { 10675400c02bSMarkus Armbruster .name = TYPE_IVSHMEM_DOORBELL, 10685400c02bSMarkus Armbruster .version_id = 0, 10695400c02bSMarkus Armbruster .minimum_version_id = 0, 10705400c02bSMarkus Armbruster .pre_load = ivshmem_pre_load, 10715400c02bSMarkus Armbruster .post_load = ivshmem_post_load, 1072*e4ea952fSRichard Henderson .fields = (const VMStateField[]) { 10735400c02bSMarkus Armbruster VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), 10745400c02bSMarkus Armbruster VMSTATE_MSIX(parent_obj, IVShmemState), 10755400c02bSMarkus Armbruster VMSTATE_UINT32(intrstatus, IVShmemState), 10765400c02bSMarkus Armbruster VMSTATE_UINT32(intrmask, IVShmemState), 10775400c02bSMarkus Armbruster VMSTATE_END_OF_LIST() 10785400c02bSMarkus Armbruster }, 10795400c02bSMarkus Armbruster }; 10805400c02bSMarkus Armbruster 10815400c02bSMarkus Armbruster static Property ivshmem_doorbell_properties[] = { 10825400c02bSMarkus Armbruster DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), 10835400c02bSMarkus Armbruster DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1), 10845400c02bSMarkus Armbruster DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, 10855400c02bSMarkus Armbruster true), 10865400c02bSMarkus Armbruster DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), 10875400c02bSMarkus Armbruster DEFINE_PROP_END_OF_LIST(), 10885400c02bSMarkus Armbruster }; 10895400c02bSMarkus Armbruster 10905400c02bSMarkus Armbruster static void ivshmem_doorbell_init(Object *obj) 10915400c02bSMarkus Armbruster { 10925400c02bSMarkus Armbruster IVShmemState *s = IVSHMEM_DOORBELL(obj); 10935400c02bSMarkus Armbruster 10945400c02bSMarkus Armbruster s->features |= (1 << IVSHMEM_MSI); 10955400c02bSMarkus Armbruster } 10965400c02bSMarkus Armbruster 10976dc64780SMarc-André Lureau static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp) 10986dc64780SMarc-André Lureau { 10996dc64780SMarc-André Lureau IVShmemState *s = IVSHMEM_COMMON(dev); 11006dc64780SMarc-André Lureau 110130650701SAnton Nefedov if (!qemu_chr_fe_backend_connected(&s->server_chr)) { 11026dc64780SMarc-André Lureau error_setg(errp, "You must specify a 'chardev'"); 11036dc64780SMarc-André Lureau return; 11046dc64780SMarc-André Lureau } 11056dc64780SMarc-André Lureau 11066dc64780SMarc-André Lureau ivshmem_common_realize(dev, errp); 11076dc64780SMarc-André Lureau } 11086dc64780SMarc-André Lureau 11095400c02bSMarkus Armbruster static void ivshmem_doorbell_class_init(ObjectClass *klass, void *data) 11105400c02bSMarkus Armbruster { 11115400c02bSMarkus Armbruster DeviceClass *dc = DEVICE_CLASS(klass); 11126dc64780SMarc-André Lureau PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 11135400c02bSMarkus Armbruster 11146dc64780SMarc-André Lureau k->realize = ivshmem_doorbell_realize; 11154f67d30bSMarc-André Lureau device_class_set_props(dc, ivshmem_doorbell_properties); 11165400c02bSMarkus Armbruster dc->vmsd = &ivshmem_doorbell_vmsd; 11175400c02bSMarkus Armbruster } 11185400c02bSMarkus Armbruster 11195400c02bSMarkus Armbruster static const TypeInfo ivshmem_doorbell_info = { 11205400c02bSMarkus Armbruster .name = TYPE_IVSHMEM_DOORBELL, 11215400c02bSMarkus Armbruster .parent = TYPE_IVSHMEM_COMMON, 11225400c02bSMarkus Armbruster .instance_size = sizeof(IVShmemState), 11235400c02bSMarkus Armbruster .instance_init = ivshmem_doorbell_init, 11245400c02bSMarkus Armbruster .class_init = ivshmem_doorbell_class_init, 11255400c02bSMarkus Armbruster }; 11265400c02bSMarkus Armbruster 112783f7d43aSAndreas Färber static void ivshmem_register_types(void) 11286cbf4c8cSCam Macdonell { 11295400c02bSMarkus Armbruster type_register_static(&ivshmem_common_info); 11305400c02bSMarkus Armbruster type_register_static(&ivshmem_plain_info); 11315400c02bSMarkus Armbruster type_register_static(&ivshmem_doorbell_info); 11326cbf4c8cSCam Macdonell } 11336cbf4c8cSCam Macdonell 113483f7d43aSAndreas Färber type_init(ivshmem_register_types) 1135