xref: /qemu/hw/pci/msix.c (revision 15377f6e79cc6aa08dbafe82607e0bda13ca44b5)
102eb84d0SMichael S. Tsirkin /*
202eb84d0SMichael S. Tsirkin  * MSI-X device support
302eb84d0SMichael S. Tsirkin  *
402eb84d0SMichael S. Tsirkin  * This module includes support for MSI-X in pci devices.
502eb84d0SMichael S. Tsirkin  *
602eb84d0SMichael S. Tsirkin  * Author: Michael S. Tsirkin <mst@redhat.com>
702eb84d0SMichael S. Tsirkin  *
802eb84d0SMichael S. Tsirkin  *  Copyright (c) 2009, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com)
902eb84d0SMichael S. Tsirkin  *
1002eb84d0SMichael S. Tsirkin  * This work is licensed under the terms of the GNU GPL, version 2.  See
1102eb84d0SMichael S. Tsirkin  * the COPYING file in the top-level directory.
126b620ca3SPaolo Bonzini  *
136b620ca3SPaolo Bonzini  * Contributions after 2012-01-13 are licensed under the terms of the
146b620ca3SPaolo Bonzini  * GNU GPL, version 2 or (at your option) any later version.
1502eb84d0SMichael S. Tsirkin  */
1602eb84d0SMichael S. Tsirkin 
1797d5408fSPeter Maydell #include "qemu/osdep.h"
18c759b24fSMichael S. Tsirkin #include "hw/pci/msi.h"
19c759b24fSMichael S. Tsirkin #include "hw/pci/msix.h"
20c759b24fSMichael S. Tsirkin #include "hw/pci/pci.h"
21428c3eceSStefano Stabellini #include "hw/xen/xen.h"
22da278d58SPhilippe Mathieu-Daudé #include "sysemu/xen.h"
23ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h"
24d6454270SMarkus Armbruster #include "migration/vmstate.h"
251de7afc9SPaolo Bonzini #include "qemu/range.h"
26ee640c62SCao jin #include "qapi/error.h"
27993b1f4bSPeter Xu #include "trace.h"
2802eb84d0SMichael S. Tsirkin 
292760952bSMichael S. Tsirkin /* MSI enable bit and maskall bit are in byte 1 in FLAGS register */
302760952bSMichael S. Tsirkin #define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1)
3102eb84d0SMichael S. Tsirkin #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8)
325b5cb086SMichael S. Tsirkin #define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8)
3302eb84d0SMichael S. Tsirkin 
3408cf3dc6SJagannathan Raman static MSIMessage msix_prepare_message(PCIDevice *dev, unsigned vector)
35bc4caf49SJan Kiszka {
36d35e428cSAlex Williamson     uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE;
37bc4caf49SJan Kiszka     MSIMessage msg;
38bc4caf49SJan Kiszka 
39bc4caf49SJan Kiszka     msg.address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR);
40bc4caf49SJan Kiszka     msg.data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA);
41bc4caf49SJan Kiszka     return msg;
42bc4caf49SJan Kiszka }
4302eb84d0SMichael S. Tsirkin 
4408cf3dc6SJagannathan Raman MSIMessage msix_get_message(PCIDevice *dev, unsigned vector)
4508cf3dc6SJagannathan Raman {
4608cf3dc6SJagannathan Raman     return dev->msix_prepare_message(dev, vector);
4708cf3dc6SJagannathan Raman }
4808cf3dc6SJagannathan Raman 
49932d4a42SAlexey Kardashevskiy /*
50932d4a42SAlexey Kardashevskiy  * Special API for POWER to configure the vectors through
51932d4a42SAlexey Kardashevskiy  * a side channel. Should never be used by devices.
52932d4a42SAlexey Kardashevskiy  */
53932d4a42SAlexey Kardashevskiy void msix_set_message(PCIDevice *dev, int vector, struct MSIMessage msg)
54932d4a42SAlexey Kardashevskiy {
55932d4a42SAlexey Kardashevskiy     uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE;
56932d4a42SAlexey Kardashevskiy 
57932d4a42SAlexey Kardashevskiy     pci_set_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR, msg.address);
58932d4a42SAlexey Kardashevskiy     pci_set_long(table_entry + PCI_MSIX_ENTRY_DATA, msg.data);
59932d4a42SAlexey Kardashevskiy     table_entry[PCI_MSIX_ENTRY_VECTOR_CTRL] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
60932d4a42SAlexey Kardashevskiy }
61932d4a42SAlexey Kardashevskiy 
6202eb84d0SMichael S. Tsirkin static uint8_t msix_pending_mask(int vector)
6302eb84d0SMichael S. Tsirkin {
6402eb84d0SMichael S. Tsirkin     return 1 << (vector % 8);
6502eb84d0SMichael S. Tsirkin }
6602eb84d0SMichael S. Tsirkin 
6702eb84d0SMichael S. Tsirkin static uint8_t *msix_pending_byte(PCIDevice *dev, int vector)
6802eb84d0SMichael S. Tsirkin {
69d35e428cSAlex Williamson     return dev->msix_pba + vector / 8;
7002eb84d0SMichael S. Tsirkin }
7102eb84d0SMichael S. Tsirkin 
7202eb84d0SMichael S. Tsirkin static int msix_is_pending(PCIDevice *dev, int vector)
7302eb84d0SMichael S. Tsirkin {
7402eb84d0SMichael S. Tsirkin     return *msix_pending_byte(dev, vector) & msix_pending_mask(vector);
7502eb84d0SMichael S. Tsirkin }
7602eb84d0SMichael S. Tsirkin 
7770f8ee39SMichael S. Tsirkin void msix_set_pending(PCIDevice *dev, unsigned int vector)
7802eb84d0SMichael S. Tsirkin {
7902eb84d0SMichael S. Tsirkin     *msix_pending_byte(dev, vector) |= msix_pending_mask(vector);
8002eb84d0SMichael S. Tsirkin }
8102eb84d0SMichael S. Tsirkin 
823bdfaabbSDmitry Fleytman void msix_clr_pending(PCIDevice *dev, int vector)
8302eb84d0SMichael S. Tsirkin {
8402eb84d0SMichael S. Tsirkin     *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector);
8502eb84d0SMichael S. Tsirkin }
8602eb84d0SMichael S. Tsirkin 
8770f8ee39SMichael S. Tsirkin static bool msix_vector_masked(PCIDevice *dev, unsigned int vector, bool fmask)
8802eb84d0SMichael S. Tsirkin {
89428c3eceSStefano Stabellini     unsigned offset = vector * PCI_MSIX_ENTRY_SIZE;
90e1e4bf22SMichael S. Tsirkin     uint8_t *data = &dev->msix_table[offset + PCI_MSIX_ENTRY_DATA];
91428c3eceSStefano Stabellini     /* MSIs on Xen can be remapped into pirqs. In those cases, masking
92428c3eceSStefano Stabellini      * and unmasking go through the PV evtchn path. */
93e1e4bf22SMichael S. Tsirkin     if (xen_enabled() && xen_is_pirq_msi(pci_get_long(data))) {
94428c3eceSStefano Stabellini         return false;
95428c3eceSStefano Stabellini     }
96428c3eceSStefano Stabellini     return fmask || dev->msix_table[offset + PCI_MSIX_ENTRY_VECTOR_CTRL] &
97428c3eceSStefano Stabellini         PCI_MSIX_ENTRY_CTRL_MASKBIT;
985b5cb086SMichael S. Tsirkin }
995b5cb086SMichael S. Tsirkin 
10070f8ee39SMichael S. Tsirkin bool msix_is_masked(PCIDevice *dev, unsigned int vector)
1015b5cb086SMichael S. Tsirkin {
102ae392c41SMichael S. Tsirkin     return msix_vector_masked(dev, vector, dev->msix_function_masked);
103ae392c41SMichael S. Tsirkin }
104ae392c41SMichael S. Tsirkin 
1052cdfe53cSJan Kiszka static void msix_fire_vector_notifier(PCIDevice *dev,
1062cdfe53cSJan Kiszka                                       unsigned int vector, bool is_masked)
1072cdfe53cSJan Kiszka {
1082cdfe53cSJan Kiszka     MSIMessage msg;
1092cdfe53cSJan Kiszka     int ret;
1102cdfe53cSJan Kiszka 
1112cdfe53cSJan Kiszka     if (!dev->msix_vector_use_notifier) {
1122cdfe53cSJan Kiszka         return;
1132cdfe53cSJan Kiszka     }
1142cdfe53cSJan Kiszka     if (is_masked) {
1152cdfe53cSJan Kiszka         dev->msix_vector_release_notifier(dev, vector);
1162cdfe53cSJan Kiszka     } else {
1172cdfe53cSJan Kiszka         msg = msix_get_message(dev, vector);
1182cdfe53cSJan Kiszka         ret = dev->msix_vector_use_notifier(dev, vector, msg);
1192cdfe53cSJan Kiszka         assert(ret >= 0);
1202cdfe53cSJan Kiszka     }
1212cdfe53cSJan Kiszka }
1222cdfe53cSJan Kiszka 
123ae392c41SMichael S. Tsirkin static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked)
124ae392c41SMichael S. Tsirkin {
125ae392c41SMichael S. Tsirkin     bool is_masked = msix_is_masked(dev, vector);
1262cdfe53cSJan Kiszka 
127ae392c41SMichael S. Tsirkin     if (is_masked == was_masked) {
128ae392c41SMichael S. Tsirkin         return;
129ae392c41SMichael S. Tsirkin     }
130ae392c41SMichael S. Tsirkin 
1312cdfe53cSJan Kiszka     msix_fire_vector_notifier(dev, vector, is_masked);
1322cdfe53cSJan Kiszka 
133ae392c41SMichael S. Tsirkin     if (!is_masked && msix_is_pending(dev, vector)) {
1345b5cb086SMichael S. Tsirkin         msix_clr_pending(dev, vector);
1355b5cb086SMichael S. Tsirkin         msix_notify(dev, vector);
1365b5cb086SMichael S. Tsirkin     }
1375b5cb086SMichael S. Tsirkin }
1385b5cb086SMichael S. Tsirkin 
139*15377f6eSAkihiko Odaki void msix_set_mask(PCIDevice *dev, int vector, bool mask)
14008cf3dc6SJagannathan Raman {
14108cf3dc6SJagannathan Raman     unsigned offset;
14208cf3dc6SJagannathan Raman     bool was_masked;
14308cf3dc6SJagannathan Raman 
144*15377f6eSAkihiko Odaki     assert(vector < dev->msix_entries_nr);
14508cf3dc6SJagannathan Raman 
14608cf3dc6SJagannathan Raman     offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
14708cf3dc6SJagannathan Raman 
14808cf3dc6SJagannathan Raman     was_masked = msix_is_masked(dev, vector);
14908cf3dc6SJagannathan Raman 
15008cf3dc6SJagannathan Raman     if (mask) {
15108cf3dc6SJagannathan Raman         dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
15208cf3dc6SJagannathan Raman     } else {
15308cf3dc6SJagannathan Raman         dev->msix_table[offset] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
15408cf3dc6SJagannathan Raman     }
15508cf3dc6SJagannathan Raman 
15608cf3dc6SJagannathan Raman     msix_handle_mask_update(dev, vector, was_masked);
15708cf3dc6SJagannathan Raman }
15808cf3dc6SJagannathan Raman 
159993b1f4bSPeter Xu static bool msix_masked(PCIDevice *dev)
160993b1f4bSPeter Xu {
161993b1f4bSPeter Xu     return dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK;
162993b1f4bSPeter Xu }
163993b1f4bSPeter Xu 
16450322249SMichael S. Tsirkin static void msix_update_function_masked(PCIDevice *dev)
16550322249SMichael S. Tsirkin {
166993b1f4bSPeter Xu     dev->msix_function_masked = !msix_enabled(dev) || msix_masked(dev);
16750322249SMichael S. Tsirkin }
16850322249SMichael S. Tsirkin 
1695b5cb086SMichael S. Tsirkin /* Handle MSI-X capability config write. */
1705b5cb086SMichael S. Tsirkin void msix_write_config(PCIDevice *dev, uint32_t addr,
1715b5cb086SMichael S. Tsirkin                        uint32_t val, int len)
1725b5cb086SMichael S. Tsirkin {
1735b5cb086SMichael S. Tsirkin     unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET;
1745b5cb086SMichael S. Tsirkin     int vector;
17550322249SMichael S. Tsirkin     bool was_masked;
1765b5cb086SMichael S. Tsirkin 
1777c9958b0SJan Kiszka     if (!msix_present(dev) || !range_covers_byte(addr, len, enable_pos)) {
1785b5cb086SMichael S. Tsirkin         return;
1795b5cb086SMichael S. Tsirkin     }
1805b5cb086SMichael S. Tsirkin 
181993b1f4bSPeter Xu     trace_msix_write_config(dev->name, msix_enabled(dev), msix_masked(dev));
182993b1f4bSPeter Xu 
18350322249SMichael S. Tsirkin     was_masked = dev->msix_function_masked;
18450322249SMichael S. Tsirkin     msix_update_function_masked(dev);
18550322249SMichael S. Tsirkin 
1865b5cb086SMichael S. Tsirkin     if (!msix_enabled(dev)) {
1875b5cb086SMichael S. Tsirkin         return;
1885b5cb086SMichael S. Tsirkin     }
1895b5cb086SMichael S. Tsirkin 
190e407bf13SIsaku Yamahata     pci_device_deassert_intx(dev);
1915b5cb086SMichael S. Tsirkin 
19250322249SMichael S. Tsirkin     if (dev->msix_function_masked == was_masked) {
1935b5cb086SMichael S. Tsirkin         return;
1945b5cb086SMichael S. Tsirkin     }
1955b5cb086SMichael S. Tsirkin 
1965b5cb086SMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
197ae392c41SMichael S. Tsirkin         msix_handle_mask_update(dev, vector,
198ae392c41SMichael S. Tsirkin                                 msix_vector_masked(dev, vector, was_masked));
1995b5cb086SMichael S. Tsirkin     }
20002eb84d0SMichael S. Tsirkin }
20102eb84d0SMichael S. Tsirkin 
202a8170e5eSAvi Kivity static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr,
203eebcb0a7SAlex Williamson                                      unsigned size)
204eebcb0a7SAlex Williamson {
205eebcb0a7SAlex Williamson     PCIDevice *dev = opaque;
206eebcb0a7SAlex Williamson 
20758cf0f86SPaolo Bonzini     assert(addr + size <= dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
208d35e428cSAlex Williamson     return pci_get_long(dev->msix_table + addr);
209eebcb0a7SAlex Williamson }
210eebcb0a7SAlex Williamson 
211a8170e5eSAvi Kivity static void msix_table_mmio_write(void *opaque, hwaddr addr,
21295524ae8SAvi Kivity                                   uint64_t val, unsigned size)
21302eb84d0SMichael S. Tsirkin {
21402eb84d0SMichael S. Tsirkin     PCIDevice *dev = opaque;
215d35e428cSAlex Williamson     int vector = addr / PCI_MSIX_ENTRY_SIZE;
216ae392c41SMichael S. Tsirkin     bool was_masked;
2179a93b617SMichael S. Tsirkin 
21858cf0f86SPaolo Bonzini     assert(addr + size <= dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
21958cf0f86SPaolo Bonzini 
220ae392c41SMichael S. Tsirkin     was_masked = msix_is_masked(dev, vector);
221d35e428cSAlex Williamson     pci_set_long(dev->msix_table + addr, val);
222ae392c41SMichael S. Tsirkin     msix_handle_mask_update(dev, vector, was_masked);
22302eb84d0SMichael S. Tsirkin }
22402eb84d0SMichael S. Tsirkin 
225d35e428cSAlex Williamson static const MemoryRegionOps msix_table_mmio_ops = {
226d35e428cSAlex Williamson     .read = msix_table_mmio_read,
227d35e428cSAlex Williamson     .write = msix_table_mmio_write,
22868d1e1f5SAlexander Graf     .endianness = DEVICE_LITTLE_ENDIAN,
229d35e428cSAlex Williamson     .valid = {
230d35e428cSAlex Williamson         .min_access_size = 4,
231191f90cbSMichael S. Tsirkin         .max_access_size = 8,
232191f90cbSMichael S. Tsirkin     },
233191f90cbSMichael S. Tsirkin     .impl = {
234d35e428cSAlex Williamson         .max_access_size = 4,
235d35e428cSAlex Williamson     },
236d35e428cSAlex Williamson };
237d35e428cSAlex Williamson 
238a8170e5eSAvi Kivity static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr,
239d35e428cSAlex Williamson                                    unsigned size)
240d35e428cSAlex Williamson {
241d35e428cSAlex Williamson     PCIDevice *dev = opaque;
242bbef882cSMichael S. Tsirkin     if (dev->msix_vector_poll_notifier) {
243bbef882cSMichael S. Tsirkin         unsigned vector_start = addr * 8;
244bbef882cSMichael S. Tsirkin         unsigned vector_end = MIN(addr + size * 8, dev->msix_entries_nr);
245bbef882cSMichael S. Tsirkin         dev->msix_vector_poll_notifier(dev, vector_start, vector_end);
246bbef882cSMichael S. Tsirkin     }
247d35e428cSAlex Williamson 
248d35e428cSAlex Williamson     return pci_get_long(dev->msix_pba + addr);
249d35e428cSAlex Williamson }
250d35e428cSAlex Williamson 
25143b11a91SMarc-André Lureau static void msix_pba_mmio_write(void *opaque, hwaddr addr,
25243b11a91SMarc-André Lureau                                 uint64_t val, unsigned size)
25343b11a91SMarc-André Lureau {
25443b11a91SMarc-André Lureau }
25543b11a91SMarc-André Lureau 
256d35e428cSAlex Williamson static const MemoryRegionOps msix_pba_mmio_ops = {
257d35e428cSAlex Williamson     .read = msix_pba_mmio_read,
25843b11a91SMarc-André Lureau     .write = msix_pba_mmio_write,
25968d1e1f5SAlexander Graf     .endianness = DEVICE_LITTLE_ENDIAN,
26095524ae8SAvi Kivity     .valid = {
26195524ae8SAvi Kivity         .min_access_size = 4,
262191f90cbSMichael S. Tsirkin         .max_access_size = 8,
263191f90cbSMichael S. Tsirkin     },
264191f90cbSMichael S. Tsirkin     .impl = {
26595524ae8SAvi Kivity         .max_access_size = 4,
26695524ae8SAvi Kivity     },
26702eb84d0SMichael S. Tsirkin };
26802eb84d0SMichael S. Tsirkin 
269ae1be0bbSMichael S. Tsirkin static void msix_mask_all(struct PCIDevice *dev, unsigned nentries)
270ae1be0bbSMichael S. Tsirkin {
271ae1be0bbSMichael S. Tsirkin     int vector;
2725b5f1330SJan Kiszka 
273ae1be0bbSMichael S. Tsirkin     for (vector = 0; vector < nentries; ++vector) {
27401731cfbSJan Kiszka         unsigned offset =
27501731cfbSJan Kiszka             vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
2765b5f1330SJan Kiszka         bool was_masked = msix_is_masked(dev, vector);
2775b5f1330SJan Kiszka 
278d35e428cSAlex Williamson         dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
2795b5f1330SJan Kiszka         msix_handle_mask_update(dev, vector, was_masked);
280ae1be0bbSMichael S. Tsirkin     }
281ae1be0bbSMichael S. Tsirkin }
282ae1be0bbSMichael S. Tsirkin 
283ee640c62SCao jin /*
284ee640c62SCao jin  * Make PCI device @dev MSI-X capable
285ee640c62SCao jin  * @nentries is the max number of MSI-X vectors that the device support.
286ee640c62SCao jin  * @table_bar is the MemoryRegion that MSI-X table structure resides.
287ee640c62SCao jin  * @table_bar_nr is number of base address register corresponding to @table_bar.
288ee640c62SCao jin  * @table_offset indicates the offset that the MSI-X table structure starts with
289ee640c62SCao jin  * in @table_bar.
290ee640c62SCao jin  * @pba_bar is the MemoryRegion that the Pending Bit Array structure resides.
291ee640c62SCao jin  * @pba_bar_nr is number of base address register corresponding to @pba_bar.
292ee640c62SCao jin  * @pba_offset indicates the offset that the Pending Bit Array structure
293ee640c62SCao jin  * starts with in @pba_bar.
294ee640c62SCao jin  * Non-zero @cap_pos puts capability MSI-X at that offset in PCI config space.
295ee640c62SCao jin  * @errp is for returning errors.
296ee640c62SCao jin  *
297ee640c62SCao jin  * Return 0 on success; set @errp and return -errno on error:
298ee640c62SCao jin  * -ENOTSUP means lacking msi support for a msi-capable platform.
299ee640c62SCao jin  * -EINVAL means capability overlap, happens when @cap_pos is non-zero,
300ee640c62SCao jin  * also means a programming error, except device assignment, which can check
301ee640c62SCao jin  * if a real HW is broken.
302ee640c62SCao jin  */
30302eb84d0SMichael S. Tsirkin int msix_init(struct PCIDevice *dev, unsigned short nentries,
3045a2c2029SAlex Williamson               MemoryRegion *table_bar, uint8_t table_bar_nr,
3055a2c2029SAlex Williamson               unsigned table_offset, MemoryRegion *pba_bar,
306ee640c62SCao jin               uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos,
307ee640c62SCao jin               Error **errp)
30802eb84d0SMichael S. Tsirkin {
3095a2c2029SAlex Williamson     int cap;
310d35e428cSAlex Williamson     unsigned table_size, pba_size;
3115a2c2029SAlex Williamson     uint8_t *config;
31202eb84d0SMichael S. Tsirkin 
31360ba3cc2SJan Kiszka     /* Nothing to do if MSI is not supported by interrupt controller */
314226419d6SMichael S. Tsirkin     if (!msi_nonbroken) {
315ee640c62SCao jin         error_setg(errp, "MSI-X is not supported by interrupt controller");
31660ba3cc2SJan Kiszka         return -ENOTSUP;
31760ba3cc2SJan Kiszka     }
3185a2c2029SAlex Williamson 
3195a2c2029SAlex Williamson     if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) {
320ee640c62SCao jin         error_setg(errp, "The number of MSI-X vectors is invalid");
32102eb84d0SMichael S. Tsirkin         return -EINVAL;
3225a2c2029SAlex Williamson     }
32302eb84d0SMichael S. Tsirkin 
324d35e428cSAlex Williamson     table_size = nentries * PCI_MSIX_ENTRY_SIZE;
325d35e428cSAlex Williamson     pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
326d35e428cSAlex Williamson 
3275a2c2029SAlex Williamson     /* Sanity test: table & pba don't overlap, fit within BARs, min aligned */
3285a2c2029SAlex Williamson     if ((table_bar_nr == pba_bar_nr &&
3295a2c2029SAlex Williamson          ranges_overlap(table_offset, table_size, pba_offset, pba_size)) ||
3305a2c2029SAlex Williamson         table_offset + table_size > memory_region_size(table_bar) ||
3315a2c2029SAlex Williamson         pba_offset + pba_size > memory_region_size(pba_bar) ||
3325a2c2029SAlex Williamson         (table_offset | pba_offset) & PCI_MSIX_FLAGS_BIRMASK) {
333ee640c62SCao jin         error_setg(errp, "table & pba overlap, or they don't fit in BARs,"
334ee640c62SCao jin                    " or don't align");
3355a2c2029SAlex Williamson         return -EINVAL;
3365a2c2029SAlex Williamson     }
3375a2c2029SAlex Williamson 
33827841278SMao Zhongyi     cap = pci_add_capability(dev, PCI_CAP_ID_MSIX,
339ee640c62SCao jin                               cap_pos, MSIX_CAP_LENGTH, errp);
3405a2c2029SAlex Williamson     if (cap < 0) {
3415a2c2029SAlex Williamson         return cap;
3425a2c2029SAlex Williamson     }
3435a2c2029SAlex Williamson 
3445a2c2029SAlex Williamson     dev->msix_cap = cap;
3455a2c2029SAlex Williamson     dev->cap_present |= QEMU_PCI_CAP_MSIX;
3465a2c2029SAlex Williamson     config = dev->config + cap;
3475a2c2029SAlex Williamson 
3485a2c2029SAlex Williamson     pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1);
3495a2c2029SAlex Williamson     dev->msix_entries_nr = nentries;
3505a2c2029SAlex Williamson     dev->msix_function_masked = true;
3515a2c2029SAlex Williamson 
3525a2c2029SAlex Williamson     pci_set_long(config + PCI_MSIX_TABLE, table_offset | table_bar_nr);
3535a2c2029SAlex Williamson     pci_set_long(config + PCI_MSIX_PBA, pba_offset | pba_bar_nr);
3545a2c2029SAlex Williamson 
3555a2c2029SAlex Williamson     /* Make flags bit writable. */
3565a2c2029SAlex Williamson     dev->wmask[cap + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK |
3575a2c2029SAlex Williamson                                              MSIX_MASKALL_MASK;
35802eb84d0SMichael S. Tsirkin 
359d35e428cSAlex Williamson     dev->msix_table = g_malloc0(table_size);
360d35e428cSAlex Williamson     dev->msix_pba = g_malloc0(pba_size);
3615a2c2029SAlex Williamson     dev->msix_entry_used = g_malloc0(nentries * sizeof *dev->msix_entry_used);
3625a2c2029SAlex Williamson 
363ae1be0bbSMichael S. Tsirkin     msix_mask_all(dev, nentries);
36402eb84d0SMichael S. Tsirkin 
36540c5dce9SPaolo Bonzini     memory_region_init_io(&dev->msix_table_mmio, OBJECT(dev), &msix_table_mmio_ops, dev,
366d35e428cSAlex Williamson                           "msix-table", table_size);
3675a2c2029SAlex Williamson     memory_region_add_subregion(table_bar, table_offset, &dev->msix_table_mmio);
36840c5dce9SPaolo Bonzini     memory_region_init_io(&dev->msix_pba_mmio, OBJECT(dev), &msix_pba_mmio_ops, dev,
369d35e428cSAlex Williamson                           "msix-pba", pba_size);
3705a2c2029SAlex Williamson     memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio);
37102eb84d0SMichael S. Tsirkin 
37208cf3dc6SJagannathan Raman     dev->msix_prepare_message = msix_prepare_message;
37308cf3dc6SJagannathan Raman 
37402eb84d0SMichael S. Tsirkin     return 0;
37502eb84d0SMichael S. Tsirkin }
37602eb84d0SMichael S. Tsirkin 
37753f94925SAlex Williamson int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
378ee640c62SCao jin                             uint8_t bar_nr, Error **errp)
37953f94925SAlex Williamson {
38053f94925SAlex Williamson     int ret;
38153f94925SAlex Williamson     char *name;
382a0ccd212SJason Wang     uint32_t bar_size = 4096;
383a0ccd212SJason Wang     uint32_t bar_pba_offset = bar_size / 2;
38417323e8bSDongli Zhang     uint32_t bar_pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
38553f94925SAlex Williamson 
38653f94925SAlex Williamson     /*
38753f94925SAlex Williamson      * Migration compatibility dictates that this remains a 4k
38853f94925SAlex Williamson      * BAR with the vector table in the lower half and PBA in
389a0ccd212SJason Wang      * the upper half for nentries which is lower or equal to 128.
390a0ccd212SJason Wang      * No need to care about using more than 65 entries for legacy
391a0ccd212SJason Wang      * machine types who has at most 64 queues.
39253f94925SAlex Williamson      */
393a0ccd212SJason Wang     if (nentries * PCI_MSIX_ENTRY_SIZE > bar_pba_offset) {
394a0ccd212SJason Wang         bar_pba_offset = nentries * PCI_MSIX_ENTRY_SIZE;
395a0ccd212SJason Wang     }
39653f94925SAlex Williamson 
397a0ccd212SJason Wang     if (bar_pba_offset + bar_pba_size > 4096) {
398a0ccd212SJason Wang         bar_size = bar_pba_offset + bar_pba_size;
399a0ccd212SJason Wang     }
400a0ccd212SJason Wang 
4019bff5d81SPeter Maydell     bar_size = pow2ceil(bar_size);
40253f94925SAlex Williamson 
4035f893b4eSGerd Hoffmann     name = g_strdup_printf("%s-msix", dev->name);
404a0ccd212SJason Wang     memory_region_init(&dev->msix_exclusive_bar, OBJECT(dev), name, bar_size);
4055f893b4eSGerd Hoffmann     g_free(name);
40653f94925SAlex Williamson 
40753f94925SAlex Williamson     ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr,
408a0ccd212SJason Wang                     0, &dev->msix_exclusive_bar,
409a0ccd212SJason Wang                     bar_nr, bar_pba_offset,
410ee640c62SCao jin                     0, errp);
41153f94925SAlex Williamson     if (ret) {
41253f94925SAlex Williamson         return ret;
41353f94925SAlex Williamson     }
41453f94925SAlex Williamson 
41553f94925SAlex Williamson     pci_register_bar(dev, bar_nr, PCI_BASE_ADDRESS_SPACE_MEMORY,
41653f94925SAlex Williamson                      &dev->msix_exclusive_bar);
41753f94925SAlex Williamson 
41853f94925SAlex Williamson     return 0;
41953f94925SAlex Williamson }
42053f94925SAlex Williamson 
42198304c84SMichael S. Tsirkin static void msix_free_irq_entries(PCIDevice *dev)
42298304c84SMichael S. Tsirkin {
42398304c84SMichael S. Tsirkin     int vector;
42498304c84SMichael S. Tsirkin 
42598304c84SMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
42698304c84SMichael S. Tsirkin         dev->msix_entry_used[vector] = 0;
42798304c84SMichael S. Tsirkin         msix_clr_pending(dev, vector);
42898304c84SMichael S. Tsirkin     }
42998304c84SMichael S. Tsirkin }
43098304c84SMichael S. Tsirkin 
4313cac001eSMichael S. Tsirkin static void msix_clear_all_vectors(PCIDevice *dev)
4323cac001eSMichael S. Tsirkin {
4333cac001eSMichael S. Tsirkin     int vector;
4343cac001eSMichael S. Tsirkin 
4353cac001eSMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
4363cac001eSMichael S. Tsirkin         msix_clr_pending(dev, vector);
4373cac001eSMichael S. Tsirkin     }
4383cac001eSMichael S. Tsirkin }
4393cac001eSMichael S. Tsirkin 
44002eb84d0SMichael S. Tsirkin /* Clean up resources for the device. */
441572992eeSAlex Williamson void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar)
44202eb84d0SMichael S. Tsirkin {
44344701ab7SJan Kiszka     if (!msix_present(dev)) {
444572992eeSAlex Williamson         return;
44544701ab7SJan Kiszka     }
44602eb84d0SMichael S. Tsirkin     pci_del_capability(dev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH);
44702eb84d0SMichael S. Tsirkin     dev->msix_cap = 0;
44802eb84d0SMichael S. Tsirkin     msix_free_irq_entries(dev);
44902eb84d0SMichael S. Tsirkin     dev->msix_entries_nr = 0;
4505a2c2029SAlex Williamson     memory_region_del_subregion(pba_bar, &dev->msix_pba_mmio);
451d35e428cSAlex Williamson     g_free(dev->msix_pba);
452d35e428cSAlex Williamson     dev->msix_pba = NULL;
4535a2c2029SAlex Williamson     memory_region_del_subregion(table_bar, &dev->msix_table_mmio);
454d35e428cSAlex Williamson     g_free(dev->msix_table);
455d35e428cSAlex Williamson     dev->msix_table = NULL;
4567267c094SAnthony Liguori     g_free(dev->msix_entry_used);
45702eb84d0SMichael S. Tsirkin     dev->msix_entry_used = NULL;
45802eb84d0SMichael S. Tsirkin     dev->cap_present &= ~QEMU_PCI_CAP_MSIX;
45908cf3dc6SJagannathan Raman     dev->msix_prepare_message = NULL;
46002eb84d0SMichael S. Tsirkin }
46102eb84d0SMichael S. Tsirkin 
46253f94925SAlex Williamson void msix_uninit_exclusive_bar(PCIDevice *dev)
46353f94925SAlex Williamson {
46453f94925SAlex Williamson     if (msix_present(dev)) {
4655a2c2029SAlex Williamson         msix_uninit(dev, &dev->msix_exclusive_bar, &dev->msix_exclusive_bar);
46653f94925SAlex Williamson     }
46753f94925SAlex Williamson }
46853f94925SAlex Williamson 
46902eb84d0SMichael S. Tsirkin void msix_save(PCIDevice *dev, QEMUFile *f)
47002eb84d0SMichael S. Tsirkin {
4719a3e12c8SMichael S. Tsirkin     unsigned n = dev->msix_entries_nr;
4729a3e12c8SMichael S. Tsirkin 
47344701ab7SJan Kiszka     if (!msix_present(dev)) {
4749a3e12c8SMichael S. Tsirkin         return;
47572755a70SMichael S. Tsirkin     }
4769a3e12c8SMichael S. Tsirkin 
477d35e428cSAlex Williamson     qemu_put_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
4780ef1efcfSMarc-André Lureau     qemu_put_buffer(f, dev->msix_pba, DIV_ROUND_UP(n, 8));
47902eb84d0SMichael S. Tsirkin }
48002eb84d0SMichael S. Tsirkin 
48102eb84d0SMichael S. Tsirkin /* Should be called after restoring the config space. */
48202eb84d0SMichael S. Tsirkin void msix_load(PCIDevice *dev, QEMUFile *f)
48302eb84d0SMichael S. Tsirkin {
48402eb84d0SMichael S. Tsirkin     unsigned n = dev->msix_entries_nr;
4852cdfe53cSJan Kiszka     unsigned int vector;
48602eb84d0SMichael S. Tsirkin 
48744701ab7SJan Kiszka     if (!msix_present(dev)) {
48802eb84d0SMichael S. Tsirkin         return;
48998846d73SBlue Swirl     }
49002eb84d0SMichael S. Tsirkin 
4913cac001eSMichael S. Tsirkin     msix_clear_all_vectors(dev);
492d35e428cSAlex Williamson     qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
4930ef1efcfSMarc-André Lureau     qemu_get_buffer(f, dev->msix_pba, DIV_ROUND_UP(n, 8));
49450322249SMichael S. Tsirkin     msix_update_function_masked(dev);
4952cdfe53cSJan Kiszka 
4962cdfe53cSJan Kiszka     for (vector = 0; vector < n; vector++) {
4972cdfe53cSJan Kiszka         msix_handle_mask_update(dev, vector, true);
4982cdfe53cSJan Kiszka     }
49902eb84d0SMichael S. Tsirkin }
50002eb84d0SMichael S. Tsirkin 
50102eb84d0SMichael S. Tsirkin /* Does device support MSI-X? */
50202eb84d0SMichael S. Tsirkin int msix_present(PCIDevice *dev)
50302eb84d0SMichael S. Tsirkin {
50402eb84d0SMichael S. Tsirkin     return dev->cap_present & QEMU_PCI_CAP_MSIX;
50502eb84d0SMichael S. Tsirkin }
50602eb84d0SMichael S. Tsirkin 
50702eb84d0SMichael S. Tsirkin /* Is MSI-X enabled? */
50802eb84d0SMichael S. Tsirkin int msix_enabled(PCIDevice *dev)
50902eb84d0SMichael S. Tsirkin {
51002eb84d0SMichael S. Tsirkin     return (dev->cap_present & QEMU_PCI_CAP_MSIX) &&
5112760952bSMichael S. Tsirkin         (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
51202eb84d0SMichael S. Tsirkin          MSIX_ENABLE_MASK);
51302eb84d0SMichael S. Tsirkin }
51402eb84d0SMichael S. Tsirkin 
51502eb84d0SMichael S. Tsirkin /* Send an MSI-X message */
51602eb84d0SMichael S. Tsirkin void msix_notify(PCIDevice *dev, unsigned vector)
51702eb84d0SMichael S. Tsirkin {
518bc4caf49SJan Kiszka     MSIMessage msg;
51902eb84d0SMichael S. Tsirkin 
520*15377f6eSAkihiko Odaki     assert(vector < dev->msix_entries_nr);
521*15377f6eSAkihiko Odaki 
522*15377f6eSAkihiko Odaki     if (!dev->msix_entry_used[vector]) {
52302eb84d0SMichael S. Tsirkin         return;
52493482436SCao jin     }
52593482436SCao jin 
52602eb84d0SMichael S. Tsirkin     if (msix_is_masked(dev, vector)) {
52702eb84d0SMichael S. Tsirkin         msix_set_pending(dev, vector);
52802eb84d0SMichael S. Tsirkin         return;
52902eb84d0SMichael S. Tsirkin     }
53002eb84d0SMichael S. Tsirkin 
531bc4caf49SJan Kiszka     msg = msix_get_message(dev, vector);
532bc4caf49SJan Kiszka 
53338d40ff1SPavel Fedin     msi_send_message(dev, msg);
53402eb84d0SMichael S. Tsirkin }
53502eb84d0SMichael S. Tsirkin 
53602eb84d0SMichael S. Tsirkin void msix_reset(PCIDevice *dev)
53702eb84d0SMichael S. Tsirkin {
53844701ab7SJan Kiszka     if (!msix_present(dev)) {
53902eb84d0SMichael S. Tsirkin         return;
54044701ab7SJan Kiszka     }
5413cac001eSMichael S. Tsirkin     msix_clear_all_vectors(dev);
5422760952bSMichael S. Tsirkin     dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &=
5432760952bSMichael S. Tsirkin             ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET];
544d35e428cSAlex Williamson     memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
545d35e428cSAlex Williamson     memset(dev->msix_pba, 0, QEMU_ALIGN_UP(dev->msix_entries_nr, 64) / 8);
546ae1be0bbSMichael S. Tsirkin     msix_mask_all(dev, dev->msix_entries_nr);
54702eb84d0SMichael S. Tsirkin }
54802eb84d0SMichael S. Tsirkin 
54902eb84d0SMichael S. Tsirkin /* PCI spec suggests that devices make it possible for software to configure
55002eb84d0SMichael S. Tsirkin  * less vectors than supported by the device, but does not specify a standard
55102eb84d0SMichael S. Tsirkin  * mechanism for devices to do so.
55202eb84d0SMichael S. Tsirkin  *
55302eb84d0SMichael S. Tsirkin  * We support this by asking devices to declare vectors software is going to
55402eb84d0SMichael S. Tsirkin  * actually use, and checking this on the notification path. Devices that
55502eb84d0SMichael S. Tsirkin  * don't want to follow the spec suggestion can declare all vectors as used. */
55602eb84d0SMichael S. Tsirkin 
55702eb84d0SMichael S. Tsirkin /* Mark vector as used. */
558*15377f6eSAkihiko Odaki void msix_vector_use(PCIDevice *dev, unsigned vector)
55902eb84d0SMichael S. Tsirkin {
560*15377f6eSAkihiko Odaki     assert(vector < dev->msix_entries_nr);
56102eb84d0SMichael S. Tsirkin     dev->msix_entry_used[vector]++;
56202eb84d0SMichael S. Tsirkin }
56302eb84d0SMichael S. Tsirkin 
56402eb84d0SMichael S. Tsirkin /* Mark vector as unused. */
56502eb84d0SMichael S. Tsirkin void msix_vector_unuse(PCIDevice *dev, unsigned vector)
56602eb84d0SMichael S. Tsirkin {
567*15377f6eSAkihiko Odaki     assert(vector < dev->msix_entries_nr);
568*15377f6eSAkihiko Odaki     if (!dev->msix_entry_used[vector]) {
56998304c84SMichael S. Tsirkin         return;
57098304c84SMichael S. Tsirkin     }
57198304c84SMichael S. Tsirkin     if (--dev->msix_entry_used[vector]) {
57298304c84SMichael S. Tsirkin         return;
57398304c84SMichael S. Tsirkin     }
57498304c84SMichael S. Tsirkin     msix_clr_pending(dev, vector);
57502eb84d0SMichael S. Tsirkin }
576b5f28bcaSMichael S. Tsirkin 
577b5f28bcaSMichael S. Tsirkin void msix_unuse_all_vectors(PCIDevice *dev)
578b5f28bcaSMichael S. Tsirkin {
57944701ab7SJan Kiszka     if (!msix_present(dev)) {
580b5f28bcaSMichael S. Tsirkin         return;
58144701ab7SJan Kiszka     }
582b5f28bcaSMichael S. Tsirkin     msix_free_irq_entries(dev);
583b5f28bcaSMichael S. Tsirkin }
5842cdfe53cSJan Kiszka 
585cb697aaaSJan Kiszka unsigned int msix_nr_vectors_allocated(const PCIDevice *dev)
586cb697aaaSJan Kiszka {
587cb697aaaSJan Kiszka     return dev->msix_entries_nr;
588cb697aaaSJan Kiszka }
589cb697aaaSJan Kiszka 
5902cdfe53cSJan Kiszka static int msix_set_notifier_for_vector(PCIDevice *dev, unsigned int vector)
5912cdfe53cSJan Kiszka {
5922cdfe53cSJan Kiszka     MSIMessage msg;
5932cdfe53cSJan Kiszka 
5942cdfe53cSJan Kiszka     if (msix_is_masked(dev, vector)) {
5952cdfe53cSJan Kiszka         return 0;
5962cdfe53cSJan Kiszka     }
5972cdfe53cSJan Kiszka     msg = msix_get_message(dev, vector);
5982cdfe53cSJan Kiszka     return dev->msix_vector_use_notifier(dev, vector, msg);
5992cdfe53cSJan Kiszka }
6002cdfe53cSJan Kiszka 
6012cdfe53cSJan Kiszka static void msix_unset_notifier_for_vector(PCIDevice *dev, unsigned int vector)
6022cdfe53cSJan Kiszka {
6032cdfe53cSJan Kiszka     if (msix_is_masked(dev, vector)) {
6042cdfe53cSJan Kiszka         return;
6052cdfe53cSJan Kiszka     }
6062cdfe53cSJan Kiszka     dev->msix_vector_release_notifier(dev, vector);
6072cdfe53cSJan Kiszka }
6082cdfe53cSJan Kiszka 
6092cdfe53cSJan Kiszka int msix_set_vector_notifiers(PCIDevice *dev,
6102cdfe53cSJan Kiszka                               MSIVectorUseNotifier use_notifier,
611bbef882cSMichael S. Tsirkin                               MSIVectorReleaseNotifier release_notifier,
612bbef882cSMichael S. Tsirkin                               MSIVectorPollNotifier poll_notifier)
6132cdfe53cSJan Kiszka {
6142cdfe53cSJan Kiszka     int vector, ret;
6152cdfe53cSJan Kiszka 
6162cdfe53cSJan Kiszka     assert(use_notifier && release_notifier);
6172cdfe53cSJan Kiszka 
6182cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = use_notifier;
6192cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = release_notifier;
620bbef882cSMichael S. Tsirkin     dev->msix_vector_poll_notifier = poll_notifier;
6212cdfe53cSJan Kiszka 
6222cdfe53cSJan Kiszka     if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
6232cdfe53cSJan Kiszka         (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) {
6242cdfe53cSJan Kiszka         for (vector = 0; vector < dev->msix_entries_nr; vector++) {
6252cdfe53cSJan Kiszka             ret = msix_set_notifier_for_vector(dev, vector);
6262cdfe53cSJan Kiszka             if (ret < 0) {
6272cdfe53cSJan Kiszka                 goto undo;
6282cdfe53cSJan Kiszka             }
6292cdfe53cSJan Kiszka         }
6302cdfe53cSJan Kiszka     }
631bbef882cSMichael S. Tsirkin     if (dev->msix_vector_poll_notifier) {
632bbef882cSMichael S. Tsirkin         dev->msix_vector_poll_notifier(dev, 0, dev->msix_entries_nr);
633bbef882cSMichael S. Tsirkin     }
6342cdfe53cSJan Kiszka     return 0;
6352cdfe53cSJan Kiszka 
6362cdfe53cSJan Kiszka undo:
6372cdfe53cSJan Kiszka     while (--vector >= 0) {
6382cdfe53cSJan Kiszka         msix_unset_notifier_for_vector(dev, vector);
6392cdfe53cSJan Kiszka     }
6402cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = NULL;
6412cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = NULL;
6422cdfe53cSJan Kiszka     return ret;
6432cdfe53cSJan Kiszka }
6442cdfe53cSJan Kiszka 
6452cdfe53cSJan Kiszka void msix_unset_vector_notifiers(PCIDevice *dev)
6462cdfe53cSJan Kiszka {
6472cdfe53cSJan Kiszka     int vector;
6482cdfe53cSJan Kiszka 
6492cdfe53cSJan Kiszka     assert(dev->msix_vector_use_notifier &&
6502cdfe53cSJan Kiszka            dev->msix_vector_release_notifier);
6512cdfe53cSJan Kiszka 
6522cdfe53cSJan Kiszka     if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
6532cdfe53cSJan Kiszka         (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) {
6542cdfe53cSJan Kiszka         for (vector = 0; vector < dev->msix_entries_nr; vector++) {
6552cdfe53cSJan Kiszka             msix_unset_notifier_for_vector(dev, vector);
6562cdfe53cSJan Kiszka         }
6572cdfe53cSJan Kiszka     }
6582cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = NULL;
6592cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = NULL;
660bbef882cSMichael S. Tsirkin     dev->msix_vector_poll_notifier = NULL;
6612cdfe53cSJan Kiszka }
662340b50c7SGerd Hoffmann 
6632c21ee76SJianjun Duan static int put_msix_state(QEMUFile *f, void *pv, size_t size,
6643ddba9a9SMarkus Armbruster                           const VMStateField *field, JSONWriter *vmdesc)
665340b50c7SGerd Hoffmann {
666340b50c7SGerd Hoffmann     msix_save(pv, f);
6672c21ee76SJianjun Duan 
6682c21ee76SJianjun Duan     return 0;
669340b50c7SGerd Hoffmann }
670340b50c7SGerd Hoffmann 
6712c21ee76SJianjun Duan static int get_msix_state(QEMUFile *f, void *pv, size_t size,
67203fee66fSMarc-André Lureau                           const VMStateField *field)
673340b50c7SGerd Hoffmann {
674340b50c7SGerd Hoffmann     msix_load(pv, f);
675340b50c7SGerd Hoffmann     return 0;
676340b50c7SGerd Hoffmann }
677340b50c7SGerd Hoffmann 
678340b50c7SGerd Hoffmann static VMStateInfo vmstate_info_msix = {
679340b50c7SGerd Hoffmann     .name = "msix state",
680340b50c7SGerd Hoffmann     .get  = get_msix_state,
681340b50c7SGerd Hoffmann     .put  = put_msix_state,
682340b50c7SGerd Hoffmann };
683340b50c7SGerd Hoffmann 
684340b50c7SGerd Hoffmann const VMStateDescription vmstate_msix = {
685340b50c7SGerd Hoffmann     .name = "msix",
686340b50c7SGerd Hoffmann     .fields = (VMStateField[]) {
687340b50c7SGerd Hoffmann         {
688340b50c7SGerd Hoffmann             .name         = "msix",
689340b50c7SGerd Hoffmann             .version_id   = 0,
690340b50c7SGerd Hoffmann             .field_exists = NULL,
691340b50c7SGerd Hoffmann             .size         = 0,   /* ouch */
692340b50c7SGerd Hoffmann             .info         = &vmstate_info_msix,
693340b50c7SGerd Hoffmann             .flags        = VMS_SINGLE,
694340b50c7SGerd Hoffmann             .offset       = 0,
695340b50c7SGerd Hoffmann         },
696340b50c7SGerd Hoffmann         VMSTATE_END_OF_LIST()
697340b50c7SGerd Hoffmann     }
698340b50c7SGerd Hoffmann };
699