xref: /qemu/hw/pci/msix.c (revision 08cf3dc611991e9697f62458107e13f2c582869a)
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 
34*08cf3dc6SJagannathan 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 
44*08cf3dc6SJagannathan Raman MSIMessage msix_get_message(PCIDevice *dev, unsigned vector)
45*08cf3dc6SJagannathan Raman {
46*08cf3dc6SJagannathan Raman     return dev->msix_prepare_message(dev, vector);
47*08cf3dc6SJagannathan Raman }
48*08cf3dc6SJagannathan 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*08cf3dc6SJagannathan Raman void msix_set_mask(PCIDevice *dev, int vector, bool mask, Error **errp)
140*08cf3dc6SJagannathan Raman {
141*08cf3dc6SJagannathan Raman     ERRP_GUARD();
142*08cf3dc6SJagannathan Raman     unsigned offset;
143*08cf3dc6SJagannathan Raman     bool was_masked;
144*08cf3dc6SJagannathan Raman 
145*08cf3dc6SJagannathan Raman     if (vector > dev->msix_entries_nr) {
146*08cf3dc6SJagannathan Raman         error_setg(errp, "msix: vector %d not allocated. max vector is %d",
147*08cf3dc6SJagannathan Raman                    vector, dev->msix_entries_nr);
148*08cf3dc6SJagannathan Raman         return;
149*08cf3dc6SJagannathan Raman     }
150*08cf3dc6SJagannathan Raman 
151*08cf3dc6SJagannathan Raman     offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
152*08cf3dc6SJagannathan Raman 
153*08cf3dc6SJagannathan Raman     was_masked = msix_is_masked(dev, vector);
154*08cf3dc6SJagannathan Raman 
155*08cf3dc6SJagannathan Raman     if (mask) {
156*08cf3dc6SJagannathan Raman         dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
157*08cf3dc6SJagannathan Raman     } else {
158*08cf3dc6SJagannathan Raman         dev->msix_table[offset] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
159*08cf3dc6SJagannathan Raman     }
160*08cf3dc6SJagannathan Raman 
161*08cf3dc6SJagannathan Raman     msix_handle_mask_update(dev, vector, was_masked);
162*08cf3dc6SJagannathan Raman }
163*08cf3dc6SJagannathan Raman 
164993b1f4bSPeter Xu static bool msix_masked(PCIDevice *dev)
165993b1f4bSPeter Xu {
166993b1f4bSPeter Xu     return dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK;
167993b1f4bSPeter Xu }
168993b1f4bSPeter Xu 
16950322249SMichael S. Tsirkin static void msix_update_function_masked(PCIDevice *dev)
17050322249SMichael S. Tsirkin {
171993b1f4bSPeter Xu     dev->msix_function_masked = !msix_enabled(dev) || msix_masked(dev);
17250322249SMichael S. Tsirkin }
17350322249SMichael S. Tsirkin 
1745b5cb086SMichael S. Tsirkin /* Handle MSI-X capability config write. */
1755b5cb086SMichael S. Tsirkin void msix_write_config(PCIDevice *dev, uint32_t addr,
1765b5cb086SMichael S. Tsirkin                        uint32_t val, int len)
1775b5cb086SMichael S. Tsirkin {
1785b5cb086SMichael S. Tsirkin     unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET;
1795b5cb086SMichael S. Tsirkin     int vector;
18050322249SMichael S. Tsirkin     bool was_masked;
1815b5cb086SMichael S. Tsirkin 
1827c9958b0SJan Kiszka     if (!msix_present(dev) || !range_covers_byte(addr, len, enable_pos)) {
1835b5cb086SMichael S. Tsirkin         return;
1845b5cb086SMichael S. Tsirkin     }
1855b5cb086SMichael S. Tsirkin 
186993b1f4bSPeter Xu     trace_msix_write_config(dev->name, msix_enabled(dev), msix_masked(dev));
187993b1f4bSPeter Xu 
18850322249SMichael S. Tsirkin     was_masked = dev->msix_function_masked;
18950322249SMichael S. Tsirkin     msix_update_function_masked(dev);
19050322249SMichael S. Tsirkin 
1915b5cb086SMichael S. Tsirkin     if (!msix_enabled(dev)) {
1925b5cb086SMichael S. Tsirkin         return;
1935b5cb086SMichael S. Tsirkin     }
1945b5cb086SMichael S. Tsirkin 
195e407bf13SIsaku Yamahata     pci_device_deassert_intx(dev);
1965b5cb086SMichael S. Tsirkin 
19750322249SMichael S. Tsirkin     if (dev->msix_function_masked == was_masked) {
1985b5cb086SMichael S. Tsirkin         return;
1995b5cb086SMichael S. Tsirkin     }
2005b5cb086SMichael S. Tsirkin 
2015b5cb086SMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
202ae392c41SMichael S. Tsirkin         msix_handle_mask_update(dev, vector,
203ae392c41SMichael S. Tsirkin                                 msix_vector_masked(dev, vector, was_masked));
2045b5cb086SMichael S. Tsirkin     }
20502eb84d0SMichael S. Tsirkin }
20602eb84d0SMichael S. Tsirkin 
207a8170e5eSAvi Kivity static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr,
208eebcb0a7SAlex Williamson                                      unsigned size)
209eebcb0a7SAlex Williamson {
210eebcb0a7SAlex Williamson     PCIDevice *dev = opaque;
211eebcb0a7SAlex Williamson 
21258cf0f86SPaolo Bonzini     assert(addr + size <= dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
213d35e428cSAlex Williamson     return pci_get_long(dev->msix_table + addr);
214eebcb0a7SAlex Williamson }
215eebcb0a7SAlex Williamson 
216a8170e5eSAvi Kivity static void msix_table_mmio_write(void *opaque, hwaddr addr,
21795524ae8SAvi Kivity                                   uint64_t val, unsigned size)
21802eb84d0SMichael S. Tsirkin {
21902eb84d0SMichael S. Tsirkin     PCIDevice *dev = opaque;
220d35e428cSAlex Williamson     int vector = addr / PCI_MSIX_ENTRY_SIZE;
221ae392c41SMichael S. Tsirkin     bool was_masked;
2229a93b617SMichael S. Tsirkin 
22358cf0f86SPaolo Bonzini     assert(addr + size <= dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
22458cf0f86SPaolo Bonzini 
225ae392c41SMichael S. Tsirkin     was_masked = msix_is_masked(dev, vector);
226d35e428cSAlex Williamson     pci_set_long(dev->msix_table + addr, val);
227ae392c41SMichael S. Tsirkin     msix_handle_mask_update(dev, vector, was_masked);
22802eb84d0SMichael S. Tsirkin }
22902eb84d0SMichael S. Tsirkin 
230d35e428cSAlex Williamson static const MemoryRegionOps msix_table_mmio_ops = {
231d35e428cSAlex Williamson     .read = msix_table_mmio_read,
232d35e428cSAlex Williamson     .write = msix_table_mmio_write,
23368d1e1f5SAlexander Graf     .endianness = DEVICE_LITTLE_ENDIAN,
234d35e428cSAlex Williamson     .valid = {
235d35e428cSAlex Williamson         .min_access_size = 4,
236191f90cbSMichael S. Tsirkin         .max_access_size = 8,
237191f90cbSMichael S. Tsirkin     },
238191f90cbSMichael S. Tsirkin     .impl = {
239d35e428cSAlex Williamson         .max_access_size = 4,
240d35e428cSAlex Williamson     },
241d35e428cSAlex Williamson };
242d35e428cSAlex Williamson 
243a8170e5eSAvi Kivity static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr,
244d35e428cSAlex Williamson                                    unsigned size)
245d35e428cSAlex Williamson {
246d35e428cSAlex Williamson     PCIDevice *dev = opaque;
247bbef882cSMichael S. Tsirkin     if (dev->msix_vector_poll_notifier) {
248bbef882cSMichael S. Tsirkin         unsigned vector_start = addr * 8;
249bbef882cSMichael S. Tsirkin         unsigned vector_end = MIN(addr + size * 8, dev->msix_entries_nr);
250bbef882cSMichael S. Tsirkin         dev->msix_vector_poll_notifier(dev, vector_start, vector_end);
251bbef882cSMichael S. Tsirkin     }
252d35e428cSAlex Williamson 
253d35e428cSAlex Williamson     return pci_get_long(dev->msix_pba + addr);
254d35e428cSAlex Williamson }
255d35e428cSAlex Williamson 
25643b11a91SMarc-André Lureau static void msix_pba_mmio_write(void *opaque, hwaddr addr,
25743b11a91SMarc-André Lureau                                 uint64_t val, unsigned size)
25843b11a91SMarc-André Lureau {
25943b11a91SMarc-André Lureau }
26043b11a91SMarc-André Lureau 
261d35e428cSAlex Williamson static const MemoryRegionOps msix_pba_mmio_ops = {
262d35e428cSAlex Williamson     .read = msix_pba_mmio_read,
26343b11a91SMarc-André Lureau     .write = msix_pba_mmio_write,
26468d1e1f5SAlexander Graf     .endianness = DEVICE_LITTLE_ENDIAN,
26595524ae8SAvi Kivity     .valid = {
26695524ae8SAvi Kivity         .min_access_size = 4,
267191f90cbSMichael S. Tsirkin         .max_access_size = 8,
268191f90cbSMichael S. Tsirkin     },
269191f90cbSMichael S. Tsirkin     .impl = {
27095524ae8SAvi Kivity         .max_access_size = 4,
27195524ae8SAvi Kivity     },
27202eb84d0SMichael S. Tsirkin };
27302eb84d0SMichael S. Tsirkin 
274ae1be0bbSMichael S. Tsirkin static void msix_mask_all(struct PCIDevice *dev, unsigned nentries)
275ae1be0bbSMichael S. Tsirkin {
276ae1be0bbSMichael S. Tsirkin     int vector;
2775b5f1330SJan Kiszka 
278ae1be0bbSMichael S. Tsirkin     for (vector = 0; vector < nentries; ++vector) {
27901731cfbSJan Kiszka         unsigned offset =
28001731cfbSJan Kiszka             vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
2815b5f1330SJan Kiszka         bool was_masked = msix_is_masked(dev, vector);
2825b5f1330SJan Kiszka 
283d35e428cSAlex Williamson         dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
2845b5f1330SJan Kiszka         msix_handle_mask_update(dev, vector, was_masked);
285ae1be0bbSMichael S. Tsirkin     }
286ae1be0bbSMichael S. Tsirkin }
287ae1be0bbSMichael S. Tsirkin 
288ee640c62SCao jin /*
289ee640c62SCao jin  * Make PCI device @dev MSI-X capable
290ee640c62SCao jin  * @nentries is the max number of MSI-X vectors that the device support.
291ee640c62SCao jin  * @table_bar is the MemoryRegion that MSI-X table structure resides.
292ee640c62SCao jin  * @table_bar_nr is number of base address register corresponding to @table_bar.
293ee640c62SCao jin  * @table_offset indicates the offset that the MSI-X table structure starts with
294ee640c62SCao jin  * in @table_bar.
295ee640c62SCao jin  * @pba_bar is the MemoryRegion that the Pending Bit Array structure resides.
296ee640c62SCao jin  * @pba_bar_nr is number of base address register corresponding to @pba_bar.
297ee640c62SCao jin  * @pba_offset indicates the offset that the Pending Bit Array structure
298ee640c62SCao jin  * starts with in @pba_bar.
299ee640c62SCao jin  * Non-zero @cap_pos puts capability MSI-X at that offset in PCI config space.
300ee640c62SCao jin  * @errp is for returning errors.
301ee640c62SCao jin  *
302ee640c62SCao jin  * Return 0 on success; set @errp and return -errno on error:
303ee640c62SCao jin  * -ENOTSUP means lacking msi support for a msi-capable platform.
304ee640c62SCao jin  * -EINVAL means capability overlap, happens when @cap_pos is non-zero,
305ee640c62SCao jin  * also means a programming error, except device assignment, which can check
306ee640c62SCao jin  * if a real HW is broken.
307ee640c62SCao jin  */
30802eb84d0SMichael S. Tsirkin int msix_init(struct PCIDevice *dev, unsigned short nentries,
3095a2c2029SAlex Williamson               MemoryRegion *table_bar, uint8_t table_bar_nr,
3105a2c2029SAlex Williamson               unsigned table_offset, MemoryRegion *pba_bar,
311ee640c62SCao jin               uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos,
312ee640c62SCao jin               Error **errp)
31302eb84d0SMichael S. Tsirkin {
3145a2c2029SAlex Williamson     int cap;
315d35e428cSAlex Williamson     unsigned table_size, pba_size;
3165a2c2029SAlex Williamson     uint8_t *config;
31702eb84d0SMichael S. Tsirkin 
31860ba3cc2SJan Kiszka     /* Nothing to do if MSI is not supported by interrupt controller */
319226419d6SMichael S. Tsirkin     if (!msi_nonbroken) {
320ee640c62SCao jin         error_setg(errp, "MSI-X is not supported by interrupt controller");
32160ba3cc2SJan Kiszka         return -ENOTSUP;
32260ba3cc2SJan Kiszka     }
3235a2c2029SAlex Williamson 
3245a2c2029SAlex Williamson     if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) {
325ee640c62SCao jin         error_setg(errp, "The number of MSI-X vectors is invalid");
32602eb84d0SMichael S. Tsirkin         return -EINVAL;
3275a2c2029SAlex Williamson     }
32802eb84d0SMichael S. Tsirkin 
329d35e428cSAlex Williamson     table_size = nentries * PCI_MSIX_ENTRY_SIZE;
330d35e428cSAlex Williamson     pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
331d35e428cSAlex Williamson 
3325a2c2029SAlex Williamson     /* Sanity test: table & pba don't overlap, fit within BARs, min aligned */
3335a2c2029SAlex Williamson     if ((table_bar_nr == pba_bar_nr &&
3345a2c2029SAlex Williamson          ranges_overlap(table_offset, table_size, pba_offset, pba_size)) ||
3355a2c2029SAlex Williamson         table_offset + table_size > memory_region_size(table_bar) ||
3365a2c2029SAlex Williamson         pba_offset + pba_size > memory_region_size(pba_bar) ||
3375a2c2029SAlex Williamson         (table_offset | pba_offset) & PCI_MSIX_FLAGS_BIRMASK) {
338ee640c62SCao jin         error_setg(errp, "table & pba overlap, or they don't fit in BARs,"
339ee640c62SCao jin                    " or don't align");
3405a2c2029SAlex Williamson         return -EINVAL;
3415a2c2029SAlex Williamson     }
3425a2c2029SAlex Williamson 
34327841278SMao Zhongyi     cap = pci_add_capability(dev, PCI_CAP_ID_MSIX,
344ee640c62SCao jin                               cap_pos, MSIX_CAP_LENGTH, errp);
3455a2c2029SAlex Williamson     if (cap < 0) {
3465a2c2029SAlex Williamson         return cap;
3475a2c2029SAlex Williamson     }
3485a2c2029SAlex Williamson 
3495a2c2029SAlex Williamson     dev->msix_cap = cap;
3505a2c2029SAlex Williamson     dev->cap_present |= QEMU_PCI_CAP_MSIX;
3515a2c2029SAlex Williamson     config = dev->config + cap;
3525a2c2029SAlex Williamson 
3535a2c2029SAlex Williamson     pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1);
3545a2c2029SAlex Williamson     dev->msix_entries_nr = nentries;
3555a2c2029SAlex Williamson     dev->msix_function_masked = true;
3565a2c2029SAlex Williamson 
3575a2c2029SAlex Williamson     pci_set_long(config + PCI_MSIX_TABLE, table_offset | table_bar_nr);
3585a2c2029SAlex Williamson     pci_set_long(config + PCI_MSIX_PBA, pba_offset | pba_bar_nr);
3595a2c2029SAlex Williamson 
3605a2c2029SAlex Williamson     /* Make flags bit writable. */
3615a2c2029SAlex Williamson     dev->wmask[cap + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK |
3625a2c2029SAlex Williamson                                              MSIX_MASKALL_MASK;
36302eb84d0SMichael S. Tsirkin 
364d35e428cSAlex Williamson     dev->msix_table = g_malloc0(table_size);
365d35e428cSAlex Williamson     dev->msix_pba = g_malloc0(pba_size);
3665a2c2029SAlex Williamson     dev->msix_entry_used = g_malloc0(nentries * sizeof *dev->msix_entry_used);
3675a2c2029SAlex Williamson 
368ae1be0bbSMichael S. Tsirkin     msix_mask_all(dev, nentries);
36902eb84d0SMichael S. Tsirkin 
37040c5dce9SPaolo Bonzini     memory_region_init_io(&dev->msix_table_mmio, OBJECT(dev), &msix_table_mmio_ops, dev,
371d35e428cSAlex Williamson                           "msix-table", table_size);
3725a2c2029SAlex Williamson     memory_region_add_subregion(table_bar, table_offset, &dev->msix_table_mmio);
37340c5dce9SPaolo Bonzini     memory_region_init_io(&dev->msix_pba_mmio, OBJECT(dev), &msix_pba_mmio_ops, dev,
374d35e428cSAlex Williamson                           "msix-pba", pba_size);
3755a2c2029SAlex Williamson     memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio);
37602eb84d0SMichael S. Tsirkin 
377*08cf3dc6SJagannathan Raman     dev->msix_prepare_message = msix_prepare_message;
378*08cf3dc6SJagannathan Raman 
37902eb84d0SMichael S. Tsirkin     return 0;
38002eb84d0SMichael S. Tsirkin }
38102eb84d0SMichael S. Tsirkin 
38253f94925SAlex Williamson int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
383ee640c62SCao jin                             uint8_t bar_nr, Error **errp)
38453f94925SAlex Williamson {
38553f94925SAlex Williamson     int ret;
38653f94925SAlex Williamson     char *name;
387a0ccd212SJason Wang     uint32_t bar_size = 4096;
388a0ccd212SJason Wang     uint32_t bar_pba_offset = bar_size / 2;
38917323e8bSDongli Zhang     uint32_t bar_pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
39053f94925SAlex Williamson 
39153f94925SAlex Williamson     /*
39253f94925SAlex Williamson      * Migration compatibility dictates that this remains a 4k
39353f94925SAlex Williamson      * BAR with the vector table in the lower half and PBA in
394a0ccd212SJason Wang      * the upper half for nentries which is lower or equal to 128.
395a0ccd212SJason Wang      * No need to care about using more than 65 entries for legacy
396a0ccd212SJason Wang      * machine types who has at most 64 queues.
39753f94925SAlex Williamson      */
398a0ccd212SJason Wang     if (nentries * PCI_MSIX_ENTRY_SIZE > bar_pba_offset) {
399a0ccd212SJason Wang         bar_pba_offset = nentries * PCI_MSIX_ENTRY_SIZE;
400a0ccd212SJason Wang     }
40153f94925SAlex Williamson 
402a0ccd212SJason Wang     if (bar_pba_offset + bar_pba_size > 4096) {
403a0ccd212SJason Wang         bar_size = bar_pba_offset + bar_pba_size;
404a0ccd212SJason Wang     }
405a0ccd212SJason Wang 
4069bff5d81SPeter Maydell     bar_size = pow2ceil(bar_size);
40753f94925SAlex Williamson 
4085f893b4eSGerd Hoffmann     name = g_strdup_printf("%s-msix", dev->name);
409a0ccd212SJason Wang     memory_region_init(&dev->msix_exclusive_bar, OBJECT(dev), name, bar_size);
4105f893b4eSGerd Hoffmann     g_free(name);
41153f94925SAlex Williamson 
41253f94925SAlex Williamson     ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr,
413a0ccd212SJason Wang                     0, &dev->msix_exclusive_bar,
414a0ccd212SJason Wang                     bar_nr, bar_pba_offset,
415ee640c62SCao jin                     0, errp);
41653f94925SAlex Williamson     if (ret) {
41753f94925SAlex Williamson         return ret;
41853f94925SAlex Williamson     }
41953f94925SAlex Williamson 
42053f94925SAlex Williamson     pci_register_bar(dev, bar_nr, PCI_BASE_ADDRESS_SPACE_MEMORY,
42153f94925SAlex Williamson                      &dev->msix_exclusive_bar);
42253f94925SAlex Williamson 
42353f94925SAlex Williamson     return 0;
42453f94925SAlex Williamson }
42553f94925SAlex Williamson 
42698304c84SMichael S. Tsirkin static void msix_free_irq_entries(PCIDevice *dev)
42798304c84SMichael S. Tsirkin {
42898304c84SMichael S. Tsirkin     int vector;
42998304c84SMichael S. Tsirkin 
43098304c84SMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
43198304c84SMichael S. Tsirkin         dev->msix_entry_used[vector] = 0;
43298304c84SMichael S. Tsirkin         msix_clr_pending(dev, vector);
43398304c84SMichael S. Tsirkin     }
43498304c84SMichael S. Tsirkin }
43598304c84SMichael S. Tsirkin 
4363cac001eSMichael S. Tsirkin static void msix_clear_all_vectors(PCIDevice *dev)
4373cac001eSMichael S. Tsirkin {
4383cac001eSMichael S. Tsirkin     int vector;
4393cac001eSMichael S. Tsirkin 
4403cac001eSMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
4413cac001eSMichael S. Tsirkin         msix_clr_pending(dev, vector);
4423cac001eSMichael S. Tsirkin     }
4433cac001eSMichael S. Tsirkin }
4443cac001eSMichael S. Tsirkin 
44502eb84d0SMichael S. Tsirkin /* Clean up resources for the device. */
446572992eeSAlex Williamson void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar)
44702eb84d0SMichael S. Tsirkin {
44844701ab7SJan Kiszka     if (!msix_present(dev)) {
449572992eeSAlex Williamson         return;
45044701ab7SJan Kiszka     }
45102eb84d0SMichael S. Tsirkin     pci_del_capability(dev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH);
45202eb84d0SMichael S. Tsirkin     dev->msix_cap = 0;
45302eb84d0SMichael S. Tsirkin     msix_free_irq_entries(dev);
45402eb84d0SMichael S. Tsirkin     dev->msix_entries_nr = 0;
4555a2c2029SAlex Williamson     memory_region_del_subregion(pba_bar, &dev->msix_pba_mmio);
456d35e428cSAlex Williamson     g_free(dev->msix_pba);
457d35e428cSAlex Williamson     dev->msix_pba = NULL;
4585a2c2029SAlex Williamson     memory_region_del_subregion(table_bar, &dev->msix_table_mmio);
459d35e428cSAlex Williamson     g_free(dev->msix_table);
460d35e428cSAlex Williamson     dev->msix_table = NULL;
4617267c094SAnthony Liguori     g_free(dev->msix_entry_used);
46202eb84d0SMichael S. Tsirkin     dev->msix_entry_used = NULL;
46302eb84d0SMichael S. Tsirkin     dev->cap_present &= ~QEMU_PCI_CAP_MSIX;
464*08cf3dc6SJagannathan Raman     dev->msix_prepare_message = NULL;
46502eb84d0SMichael S. Tsirkin }
46602eb84d0SMichael S. Tsirkin 
46753f94925SAlex Williamson void msix_uninit_exclusive_bar(PCIDevice *dev)
46853f94925SAlex Williamson {
46953f94925SAlex Williamson     if (msix_present(dev)) {
4705a2c2029SAlex Williamson         msix_uninit(dev, &dev->msix_exclusive_bar, &dev->msix_exclusive_bar);
47153f94925SAlex Williamson     }
47253f94925SAlex Williamson }
47353f94925SAlex Williamson 
47402eb84d0SMichael S. Tsirkin void msix_save(PCIDevice *dev, QEMUFile *f)
47502eb84d0SMichael S. Tsirkin {
4769a3e12c8SMichael S. Tsirkin     unsigned n = dev->msix_entries_nr;
4779a3e12c8SMichael S. Tsirkin 
47844701ab7SJan Kiszka     if (!msix_present(dev)) {
4799a3e12c8SMichael S. Tsirkin         return;
48072755a70SMichael S. Tsirkin     }
4819a3e12c8SMichael S. Tsirkin 
482d35e428cSAlex Williamson     qemu_put_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
4830ef1efcfSMarc-André Lureau     qemu_put_buffer(f, dev->msix_pba, DIV_ROUND_UP(n, 8));
48402eb84d0SMichael S. Tsirkin }
48502eb84d0SMichael S. Tsirkin 
48602eb84d0SMichael S. Tsirkin /* Should be called after restoring the config space. */
48702eb84d0SMichael S. Tsirkin void msix_load(PCIDevice *dev, QEMUFile *f)
48802eb84d0SMichael S. Tsirkin {
48902eb84d0SMichael S. Tsirkin     unsigned n = dev->msix_entries_nr;
4902cdfe53cSJan Kiszka     unsigned int vector;
49102eb84d0SMichael S. Tsirkin 
49244701ab7SJan Kiszka     if (!msix_present(dev)) {
49302eb84d0SMichael S. Tsirkin         return;
49498846d73SBlue Swirl     }
49502eb84d0SMichael S. Tsirkin 
4963cac001eSMichael S. Tsirkin     msix_clear_all_vectors(dev);
497d35e428cSAlex Williamson     qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
4980ef1efcfSMarc-André Lureau     qemu_get_buffer(f, dev->msix_pba, DIV_ROUND_UP(n, 8));
49950322249SMichael S. Tsirkin     msix_update_function_masked(dev);
5002cdfe53cSJan Kiszka 
5012cdfe53cSJan Kiszka     for (vector = 0; vector < n; vector++) {
5022cdfe53cSJan Kiszka         msix_handle_mask_update(dev, vector, true);
5032cdfe53cSJan Kiszka     }
50402eb84d0SMichael S. Tsirkin }
50502eb84d0SMichael S. Tsirkin 
50602eb84d0SMichael S. Tsirkin /* Does device support MSI-X? */
50702eb84d0SMichael S. Tsirkin int msix_present(PCIDevice *dev)
50802eb84d0SMichael S. Tsirkin {
50902eb84d0SMichael S. Tsirkin     return dev->cap_present & QEMU_PCI_CAP_MSIX;
51002eb84d0SMichael S. Tsirkin }
51102eb84d0SMichael S. Tsirkin 
51202eb84d0SMichael S. Tsirkin /* Is MSI-X enabled? */
51302eb84d0SMichael S. Tsirkin int msix_enabled(PCIDevice *dev)
51402eb84d0SMichael S. Tsirkin {
51502eb84d0SMichael S. Tsirkin     return (dev->cap_present & QEMU_PCI_CAP_MSIX) &&
5162760952bSMichael S. Tsirkin         (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
51702eb84d0SMichael S. Tsirkin          MSIX_ENABLE_MASK);
51802eb84d0SMichael S. Tsirkin }
51902eb84d0SMichael S. Tsirkin 
52002eb84d0SMichael S. Tsirkin /* Send an MSI-X message */
52102eb84d0SMichael S. Tsirkin void msix_notify(PCIDevice *dev, unsigned vector)
52202eb84d0SMichael S. Tsirkin {
523bc4caf49SJan Kiszka     MSIMessage msg;
52402eb84d0SMichael S. Tsirkin 
52593482436SCao jin     if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) {
52602eb84d0SMichael S. Tsirkin         return;
52793482436SCao jin     }
52893482436SCao jin 
52902eb84d0SMichael S. Tsirkin     if (msix_is_masked(dev, vector)) {
53002eb84d0SMichael S. Tsirkin         msix_set_pending(dev, vector);
53102eb84d0SMichael S. Tsirkin         return;
53202eb84d0SMichael S. Tsirkin     }
53302eb84d0SMichael S. Tsirkin 
534bc4caf49SJan Kiszka     msg = msix_get_message(dev, vector);
535bc4caf49SJan Kiszka 
53638d40ff1SPavel Fedin     msi_send_message(dev, msg);
53702eb84d0SMichael S. Tsirkin }
53802eb84d0SMichael S. Tsirkin 
53902eb84d0SMichael S. Tsirkin void msix_reset(PCIDevice *dev)
54002eb84d0SMichael S. Tsirkin {
54144701ab7SJan Kiszka     if (!msix_present(dev)) {
54202eb84d0SMichael S. Tsirkin         return;
54344701ab7SJan Kiszka     }
5443cac001eSMichael S. Tsirkin     msix_clear_all_vectors(dev);
5452760952bSMichael S. Tsirkin     dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &=
5462760952bSMichael S. Tsirkin             ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET];
547d35e428cSAlex Williamson     memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
548d35e428cSAlex Williamson     memset(dev->msix_pba, 0, QEMU_ALIGN_UP(dev->msix_entries_nr, 64) / 8);
549ae1be0bbSMichael S. Tsirkin     msix_mask_all(dev, dev->msix_entries_nr);
55002eb84d0SMichael S. Tsirkin }
55102eb84d0SMichael S. Tsirkin 
55202eb84d0SMichael S. Tsirkin /* PCI spec suggests that devices make it possible for software to configure
55302eb84d0SMichael S. Tsirkin  * less vectors than supported by the device, but does not specify a standard
55402eb84d0SMichael S. Tsirkin  * mechanism for devices to do so.
55502eb84d0SMichael S. Tsirkin  *
55602eb84d0SMichael S. Tsirkin  * We support this by asking devices to declare vectors software is going to
55702eb84d0SMichael S. Tsirkin  * actually use, and checking this on the notification path. Devices that
55802eb84d0SMichael S. Tsirkin  * don't want to follow the spec suggestion can declare all vectors as used. */
55902eb84d0SMichael S. Tsirkin 
56002eb84d0SMichael S. Tsirkin /* Mark vector as used. */
56102eb84d0SMichael S. Tsirkin int msix_vector_use(PCIDevice *dev, unsigned vector)
56202eb84d0SMichael S. Tsirkin {
56393482436SCao jin     if (vector >= dev->msix_entries_nr) {
56402eb84d0SMichael S. Tsirkin         return -EINVAL;
56593482436SCao jin     }
56693482436SCao jin 
56702eb84d0SMichael S. Tsirkin     dev->msix_entry_used[vector]++;
56802eb84d0SMichael S. Tsirkin     return 0;
56902eb84d0SMichael S. Tsirkin }
57002eb84d0SMichael S. Tsirkin 
57102eb84d0SMichael S. Tsirkin /* Mark vector as unused. */
57202eb84d0SMichael S. Tsirkin void msix_vector_unuse(PCIDevice *dev, unsigned vector)
57302eb84d0SMichael S. Tsirkin {
57498304c84SMichael S. Tsirkin     if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) {
57598304c84SMichael S. Tsirkin         return;
57698304c84SMichael S. Tsirkin     }
57798304c84SMichael S. Tsirkin     if (--dev->msix_entry_used[vector]) {
57898304c84SMichael S. Tsirkin         return;
57998304c84SMichael S. Tsirkin     }
58098304c84SMichael S. Tsirkin     msix_clr_pending(dev, vector);
58102eb84d0SMichael S. Tsirkin }
582b5f28bcaSMichael S. Tsirkin 
583b5f28bcaSMichael S. Tsirkin void msix_unuse_all_vectors(PCIDevice *dev)
584b5f28bcaSMichael S. Tsirkin {
58544701ab7SJan Kiszka     if (!msix_present(dev)) {
586b5f28bcaSMichael S. Tsirkin         return;
58744701ab7SJan Kiszka     }
588b5f28bcaSMichael S. Tsirkin     msix_free_irq_entries(dev);
589b5f28bcaSMichael S. Tsirkin }
5902cdfe53cSJan Kiszka 
591cb697aaaSJan Kiszka unsigned int msix_nr_vectors_allocated(const PCIDevice *dev)
592cb697aaaSJan Kiszka {
593cb697aaaSJan Kiszka     return dev->msix_entries_nr;
594cb697aaaSJan Kiszka }
595cb697aaaSJan Kiszka 
5962cdfe53cSJan Kiszka static int msix_set_notifier_for_vector(PCIDevice *dev, unsigned int vector)
5972cdfe53cSJan Kiszka {
5982cdfe53cSJan Kiszka     MSIMessage msg;
5992cdfe53cSJan Kiszka 
6002cdfe53cSJan Kiszka     if (msix_is_masked(dev, vector)) {
6012cdfe53cSJan Kiszka         return 0;
6022cdfe53cSJan Kiszka     }
6032cdfe53cSJan Kiszka     msg = msix_get_message(dev, vector);
6042cdfe53cSJan Kiszka     return dev->msix_vector_use_notifier(dev, vector, msg);
6052cdfe53cSJan Kiszka }
6062cdfe53cSJan Kiszka 
6072cdfe53cSJan Kiszka static void msix_unset_notifier_for_vector(PCIDevice *dev, unsigned int vector)
6082cdfe53cSJan Kiszka {
6092cdfe53cSJan Kiszka     if (msix_is_masked(dev, vector)) {
6102cdfe53cSJan Kiszka         return;
6112cdfe53cSJan Kiszka     }
6122cdfe53cSJan Kiszka     dev->msix_vector_release_notifier(dev, vector);
6132cdfe53cSJan Kiszka }
6142cdfe53cSJan Kiszka 
6152cdfe53cSJan Kiszka int msix_set_vector_notifiers(PCIDevice *dev,
6162cdfe53cSJan Kiszka                               MSIVectorUseNotifier use_notifier,
617bbef882cSMichael S. Tsirkin                               MSIVectorReleaseNotifier release_notifier,
618bbef882cSMichael S. Tsirkin                               MSIVectorPollNotifier poll_notifier)
6192cdfe53cSJan Kiszka {
6202cdfe53cSJan Kiszka     int vector, ret;
6212cdfe53cSJan Kiszka 
6222cdfe53cSJan Kiszka     assert(use_notifier && release_notifier);
6232cdfe53cSJan Kiszka 
6242cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = use_notifier;
6252cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = release_notifier;
626bbef882cSMichael S. Tsirkin     dev->msix_vector_poll_notifier = poll_notifier;
6272cdfe53cSJan Kiszka 
6282cdfe53cSJan Kiszka     if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
6292cdfe53cSJan Kiszka         (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) {
6302cdfe53cSJan Kiszka         for (vector = 0; vector < dev->msix_entries_nr; vector++) {
6312cdfe53cSJan Kiszka             ret = msix_set_notifier_for_vector(dev, vector);
6322cdfe53cSJan Kiszka             if (ret < 0) {
6332cdfe53cSJan Kiszka                 goto undo;
6342cdfe53cSJan Kiszka             }
6352cdfe53cSJan Kiszka         }
6362cdfe53cSJan Kiszka     }
637bbef882cSMichael S. Tsirkin     if (dev->msix_vector_poll_notifier) {
638bbef882cSMichael S. Tsirkin         dev->msix_vector_poll_notifier(dev, 0, dev->msix_entries_nr);
639bbef882cSMichael S. Tsirkin     }
6402cdfe53cSJan Kiszka     return 0;
6412cdfe53cSJan Kiszka 
6422cdfe53cSJan Kiszka undo:
6432cdfe53cSJan Kiszka     while (--vector >= 0) {
6442cdfe53cSJan Kiszka         msix_unset_notifier_for_vector(dev, vector);
6452cdfe53cSJan Kiszka     }
6462cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = NULL;
6472cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = NULL;
6482cdfe53cSJan Kiszka     return ret;
6492cdfe53cSJan Kiszka }
6502cdfe53cSJan Kiszka 
6512cdfe53cSJan Kiszka void msix_unset_vector_notifiers(PCIDevice *dev)
6522cdfe53cSJan Kiszka {
6532cdfe53cSJan Kiszka     int vector;
6542cdfe53cSJan Kiszka 
6552cdfe53cSJan Kiszka     assert(dev->msix_vector_use_notifier &&
6562cdfe53cSJan Kiszka            dev->msix_vector_release_notifier);
6572cdfe53cSJan Kiszka 
6582cdfe53cSJan Kiszka     if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
6592cdfe53cSJan Kiszka         (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) {
6602cdfe53cSJan Kiszka         for (vector = 0; vector < dev->msix_entries_nr; vector++) {
6612cdfe53cSJan Kiszka             msix_unset_notifier_for_vector(dev, vector);
6622cdfe53cSJan Kiszka         }
6632cdfe53cSJan Kiszka     }
6642cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = NULL;
6652cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = NULL;
666bbef882cSMichael S. Tsirkin     dev->msix_vector_poll_notifier = NULL;
6672cdfe53cSJan Kiszka }
668340b50c7SGerd Hoffmann 
6692c21ee76SJianjun Duan static int put_msix_state(QEMUFile *f, void *pv, size_t size,
6703ddba9a9SMarkus Armbruster                           const VMStateField *field, JSONWriter *vmdesc)
671340b50c7SGerd Hoffmann {
672340b50c7SGerd Hoffmann     msix_save(pv, f);
6732c21ee76SJianjun Duan 
6742c21ee76SJianjun Duan     return 0;
675340b50c7SGerd Hoffmann }
676340b50c7SGerd Hoffmann 
6772c21ee76SJianjun Duan static int get_msix_state(QEMUFile *f, void *pv, size_t size,
67803fee66fSMarc-André Lureau                           const VMStateField *field)
679340b50c7SGerd Hoffmann {
680340b50c7SGerd Hoffmann     msix_load(pv, f);
681340b50c7SGerd Hoffmann     return 0;
682340b50c7SGerd Hoffmann }
683340b50c7SGerd Hoffmann 
684340b50c7SGerd Hoffmann static VMStateInfo vmstate_info_msix = {
685340b50c7SGerd Hoffmann     .name = "msix state",
686340b50c7SGerd Hoffmann     .get  = get_msix_state,
687340b50c7SGerd Hoffmann     .put  = put_msix_state,
688340b50c7SGerd Hoffmann };
689340b50c7SGerd Hoffmann 
690340b50c7SGerd Hoffmann const VMStateDescription vmstate_msix = {
691340b50c7SGerd Hoffmann     .name = "msix",
692340b50c7SGerd Hoffmann     .fields = (VMStateField[]) {
693340b50c7SGerd Hoffmann         {
694340b50c7SGerd Hoffmann             .name         = "msix",
695340b50c7SGerd Hoffmann             .version_id   = 0,
696340b50c7SGerd Hoffmann             .field_exists = NULL,
697340b50c7SGerd Hoffmann             .size         = 0,   /* ouch */
698340b50c7SGerd Hoffmann             .info         = &vmstate_info_msix,
699340b50c7SGerd Hoffmann             .flags        = VMS_SINGLE,
700340b50c7SGerd Hoffmann             .offset       = 0,
701340b50c7SGerd Hoffmann         },
702340b50c7SGerd Hoffmann         VMSTATE_END_OF_LIST()
703340b50c7SGerd Hoffmann     }
704340b50c7SGerd Hoffmann };
705