xref: /qemu/hw/pci/msix.c (revision 2d37fe9e5e61b04bddbed00dbb7436e61a01c115)
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 
296096cf78SDavid Woodhouse #include "hw/i386/kvm/xen_evtchn.h"
306096cf78SDavid Woodhouse 
312760952bSMichael S. Tsirkin /* MSI enable bit and maskall bit are in byte 1 in FLAGS register */
322760952bSMichael S. Tsirkin #define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1)
3302eb84d0SMichael S. Tsirkin #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8)
345b5cb086SMichael S. Tsirkin #define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8)
3502eb84d0SMichael S. Tsirkin 
3608cf3dc6SJagannathan Raman static MSIMessage msix_prepare_message(PCIDevice *dev, unsigned vector)
37bc4caf49SJan Kiszka {
38d35e428cSAlex Williamson     uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE;
39bc4caf49SJan Kiszka     MSIMessage msg;
40bc4caf49SJan Kiszka 
41bc4caf49SJan Kiszka     msg.address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR);
42bc4caf49SJan Kiszka     msg.data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA);
43bc4caf49SJan Kiszka     return msg;
44bc4caf49SJan Kiszka }
4502eb84d0SMichael S. Tsirkin 
4608cf3dc6SJagannathan Raman MSIMessage msix_get_message(PCIDevice *dev, unsigned vector)
4708cf3dc6SJagannathan Raman {
4808cf3dc6SJagannathan Raman     return dev->msix_prepare_message(dev, vector);
4908cf3dc6SJagannathan Raman }
5008cf3dc6SJagannathan Raman 
51932d4a42SAlexey Kardashevskiy /*
52932d4a42SAlexey Kardashevskiy  * Special API for POWER to configure the vectors through
53932d4a42SAlexey Kardashevskiy  * a side channel. Should never be used by devices.
54932d4a42SAlexey Kardashevskiy  */
55932d4a42SAlexey Kardashevskiy void msix_set_message(PCIDevice *dev, int vector, struct MSIMessage msg)
56932d4a42SAlexey Kardashevskiy {
57932d4a42SAlexey Kardashevskiy     uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE;
58932d4a42SAlexey Kardashevskiy 
59932d4a42SAlexey Kardashevskiy     pci_set_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR, msg.address);
60932d4a42SAlexey Kardashevskiy     pci_set_long(table_entry + PCI_MSIX_ENTRY_DATA, msg.data);
61932d4a42SAlexey Kardashevskiy     table_entry[PCI_MSIX_ENTRY_VECTOR_CTRL] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
62932d4a42SAlexey Kardashevskiy }
63932d4a42SAlexey Kardashevskiy 
6402eb84d0SMichael S. Tsirkin static uint8_t msix_pending_mask(int vector)
6502eb84d0SMichael S. Tsirkin {
6602eb84d0SMichael S. Tsirkin     return 1 << (vector % 8);
6702eb84d0SMichael S. Tsirkin }
6802eb84d0SMichael S. Tsirkin 
6902eb84d0SMichael S. Tsirkin static uint8_t *msix_pending_byte(PCIDevice *dev, int vector)
7002eb84d0SMichael S. Tsirkin {
71d35e428cSAlex Williamson     return dev->msix_pba + vector / 8;
7202eb84d0SMichael S. Tsirkin }
7302eb84d0SMichael S. Tsirkin 
7402eb84d0SMichael S. Tsirkin static int msix_is_pending(PCIDevice *dev, int vector)
7502eb84d0SMichael S. Tsirkin {
7602eb84d0SMichael S. Tsirkin     return *msix_pending_byte(dev, vector) & msix_pending_mask(vector);
7702eb84d0SMichael S. Tsirkin }
7802eb84d0SMichael S. Tsirkin 
7970f8ee39SMichael S. Tsirkin void msix_set_pending(PCIDevice *dev, unsigned int vector)
8002eb84d0SMichael S. Tsirkin {
8102eb84d0SMichael S. Tsirkin     *msix_pending_byte(dev, vector) |= msix_pending_mask(vector);
8202eb84d0SMichael S. Tsirkin }
8302eb84d0SMichael S. Tsirkin 
843bdfaabbSDmitry Fleytman void msix_clr_pending(PCIDevice *dev, int vector)
8502eb84d0SMichael S. Tsirkin {
8602eb84d0SMichael S. Tsirkin     *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector);
8702eb84d0SMichael S. Tsirkin }
8802eb84d0SMichael S. Tsirkin 
8970f8ee39SMichael S. Tsirkin static bool msix_vector_masked(PCIDevice *dev, unsigned int vector, bool fmask)
9002eb84d0SMichael S. Tsirkin {
91428c3eceSStefano Stabellini     unsigned offset = vector * PCI_MSIX_ENTRY_SIZE;
92e1e4bf22SMichael S. Tsirkin     uint8_t *data = &dev->msix_table[offset + PCI_MSIX_ENTRY_DATA];
93428c3eceSStefano Stabellini     /* MSIs on Xen can be remapped into pirqs. In those cases, masking
94428c3eceSStefano Stabellini      * and unmasking go through the PV evtchn path. */
95e1e4bf22SMichael S. Tsirkin     if (xen_enabled() && xen_is_pirq_msi(pci_get_long(data))) {
96428c3eceSStefano Stabellini         return false;
97428c3eceSStefano Stabellini     }
98428c3eceSStefano Stabellini     return fmask || dev->msix_table[offset + PCI_MSIX_ENTRY_VECTOR_CTRL] &
99428c3eceSStefano Stabellini         PCI_MSIX_ENTRY_CTRL_MASKBIT;
1005b5cb086SMichael S. Tsirkin }
1015b5cb086SMichael S. Tsirkin 
10270f8ee39SMichael S. Tsirkin bool msix_is_masked(PCIDevice *dev, unsigned int vector)
1035b5cb086SMichael S. Tsirkin {
104ae392c41SMichael S. Tsirkin     return msix_vector_masked(dev, vector, dev->msix_function_masked);
105ae392c41SMichael S. Tsirkin }
106ae392c41SMichael S. Tsirkin 
1072cdfe53cSJan Kiszka static void msix_fire_vector_notifier(PCIDevice *dev,
1082cdfe53cSJan Kiszka                                       unsigned int vector, bool is_masked)
1092cdfe53cSJan Kiszka {
1102cdfe53cSJan Kiszka     MSIMessage msg;
1112cdfe53cSJan Kiszka     int ret;
1122cdfe53cSJan Kiszka 
1132cdfe53cSJan Kiszka     if (!dev->msix_vector_use_notifier) {
1142cdfe53cSJan Kiszka         return;
1152cdfe53cSJan Kiszka     }
1162cdfe53cSJan Kiszka     if (is_masked) {
1172cdfe53cSJan Kiszka         dev->msix_vector_release_notifier(dev, vector);
1182cdfe53cSJan Kiszka     } else {
1192cdfe53cSJan Kiszka         msg = msix_get_message(dev, vector);
1202cdfe53cSJan Kiszka         ret = dev->msix_vector_use_notifier(dev, vector, msg);
1212cdfe53cSJan Kiszka         assert(ret >= 0);
1222cdfe53cSJan Kiszka     }
1232cdfe53cSJan Kiszka }
1242cdfe53cSJan Kiszka 
125ae392c41SMichael S. Tsirkin static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked)
126ae392c41SMichael S. Tsirkin {
127ae392c41SMichael S. Tsirkin     bool is_masked = msix_is_masked(dev, vector);
1282cdfe53cSJan Kiszka 
1296096cf78SDavid Woodhouse     if (xen_mode == XEN_EMULATE) {
1306096cf78SDavid Woodhouse         MSIMessage msg = msix_prepare_message(dev, vector);
1316096cf78SDavid Woodhouse 
1326096cf78SDavid Woodhouse         xen_evtchn_snoop_msi(dev, true, vector, msg.address, msg.data,
1336096cf78SDavid Woodhouse                              is_masked);
1346096cf78SDavid Woodhouse     }
1356096cf78SDavid Woodhouse 
136ae392c41SMichael S. Tsirkin     if (is_masked == was_masked) {
137ae392c41SMichael S. Tsirkin         return;
138ae392c41SMichael S. Tsirkin     }
139ae392c41SMichael S. Tsirkin 
1402cdfe53cSJan Kiszka     msix_fire_vector_notifier(dev, vector, is_masked);
1412cdfe53cSJan Kiszka 
142ae392c41SMichael S. Tsirkin     if (!is_masked && msix_is_pending(dev, vector)) {
1435b5cb086SMichael S. Tsirkin         msix_clr_pending(dev, vector);
1445b5cb086SMichael S. Tsirkin         msix_notify(dev, vector);
1455b5cb086SMichael S. Tsirkin     }
1465b5cb086SMichael S. Tsirkin }
1475b5cb086SMichael S. Tsirkin 
14815377f6eSAkihiko Odaki void msix_set_mask(PCIDevice *dev, int vector, bool mask)
14908cf3dc6SJagannathan Raman {
15008cf3dc6SJagannathan Raman     unsigned offset;
15108cf3dc6SJagannathan Raman     bool was_masked;
15208cf3dc6SJagannathan Raman 
15315377f6eSAkihiko Odaki     assert(vector < dev->msix_entries_nr);
15408cf3dc6SJagannathan Raman 
15508cf3dc6SJagannathan Raman     offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
15608cf3dc6SJagannathan Raman 
15708cf3dc6SJagannathan Raman     was_masked = msix_is_masked(dev, vector);
15808cf3dc6SJagannathan Raman 
15908cf3dc6SJagannathan Raman     if (mask) {
16008cf3dc6SJagannathan Raman         dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
16108cf3dc6SJagannathan Raman     } else {
16208cf3dc6SJagannathan Raman         dev->msix_table[offset] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
16308cf3dc6SJagannathan Raman     }
16408cf3dc6SJagannathan Raman 
16508cf3dc6SJagannathan Raman     msix_handle_mask_update(dev, vector, was_masked);
16608cf3dc6SJagannathan Raman }
16708cf3dc6SJagannathan Raman 
168993b1f4bSPeter Xu static bool msix_masked(PCIDevice *dev)
169993b1f4bSPeter Xu {
170993b1f4bSPeter Xu     return dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK;
171993b1f4bSPeter Xu }
172993b1f4bSPeter Xu 
17350322249SMichael S. Tsirkin static void msix_update_function_masked(PCIDevice *dev)
17450322249SMichael S. Tsirkin {
175993b1f4bSPeter Xu     dev->msix_function_masked = !msix_enabled(dev) || msix_masked(dev);
17650322249SMichael S. Tsirkin }
17750322249SMichael S. Tsirkin 
1785b5cb086SMichael S. Tsirkin /* Handle MSI-X capability config write. */
1795b5cb086SMichael S. Tsirkin void msix_write_config(PCIDevice *dev, uint32_t addr,
1805b5cb086SMichael S. Tsirkin                        uint32_t val, int len)
1815b5cb086SMichael S. Tsirkin {
1825b5cb086SMichael S. Tsirkin     unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET;
1835b5cb086SMichael S. Tsirkin     int vector;
18450322249SMichael S. Tsirkin     bool was_masked;
1855b5cb086SMichael S. Tsirkin 
1867c9958b0SJan Kiszka     if (!msix_present(dev) || !range_covers_byte(addr, len, enable_pos)) {
1875b5cb086SMichael S. Tsirkin         return;
1885b5cb086SMichael S. Tsirkin     }
1895b5cb086SMichael S. Tsirkin 
190993b1f4bSPeter Xu     trace_msix_write_config(dev->name, msix_enabled(dev), msix_masked(dev));
191993b1f4bSPeter Xu 
19250322249SMichael S. Tsirkin     was_masked = dev->msix_function_masked;
19350322249SMichael S. Tsirkin     msix_update_function_masked(dev);
19450322249SMichael S. Tsirkin 
1955b5cb086SMichael S. Tsirkin     if (!msix_enabled(dev)) {
1965b5cb086SMichael S. Tsirkin         return;
1975b5cb086SMichael S. Tsirkin     }
1985b5cb086SMichael S. Tsirkin 
199e407bf13SIsaku Yamahata     pci_device_deassert_intx(dev);
2005b5cb086SMichael S. Tsirkin 
20150322249SMichael S. Tsirkin     if (dev->msix_function_masked == was_masked) {
2025b5cb086SMichael S. Tsirkin         return;
2035b5cb086SMichael S. Tsirkin     }
2045b5cb086SMichael S. Tsirkin 
2055b5cb086SMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
206ae392c41SMichael S. Tsirkin         msix_handle_mask_update(dev, vector,
207ae392c41SMichael S. Tsirkin                                 msix_vector_masked(dev, vector, was_masked));
2085b5cb086SMichael S. Tsirkin     }
20902eb84d0SMichael S. Tsirkin }
21002eb84d0SMichael S. Tsirkin 
211a8170e5eSAvi Kivity static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr,
212eebcb0a7SAlex Williamson                                      unsigned size)
213eebcb0a7SAlex Williamson {
214eebcb0a7SAlex Williamson     PCIDevice *dev = opaque;
215eebcb0a7SAlex Williamson 
21658cf0f86SPaolo Bonzini     assert(addr + size <= dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
217d35e428cSAlex Williamson     return pci_get_long(dev->msix_table + addr);
218eebcb0a7SAlex Williamson }
219eebcb0a7SAlex Williamson 
220a8170e5eSAvi Kivity static void msix_table_mmio_write(void *opaque, hwaddr addr,
22195524ae8SAvi Kivity                                   uint64_t val, unsigned size)
22202eb84d0SMichael S. Tsirkin {
22302eb84d0SMichael S. Tsirkin     PCIDevice *dev = opaque;
224d35e428cSAlex Williamson     int vector = addr / PCI_MSIX_ENTRY_SIZE;
225ae392c41SMichael S. Tsirkin     bool was_masked;
2269a93b617SMichael S. Tsirkin 
22758cf0f86SPaolo Bonzini     assert(addr + size <= dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
22858cf0f86SPaolo Bonzini 
229ae392c41SMichael S. Tsirkin     was_masked = msix_is_masked(dev, vector);
230d35e428cSAlex Williamson     pci_set_long(dev->msix_table + addr, val);
231ae392c41SMichael S. Tsirkin     msix_handle_mask_update(dev, vector, was_masked);
23202eb84d0SMichael S. Tsirkin }
23302eb84d0SMichael S. Tsirkin 
234d35e428cSAlex Williamson static const MemoryRegionOps msix_table_mmio_ops = {
235d35e428cSAlex Williamson     .read = msix_table_mmio_read,
236d35e428cSAlex Williamson     .write = msix_table_mmio_write,
23768d1e1f5SAlexander Graf     .endianness = DEVICE_LITTLE_ENDIAN,
238d35e428cSAlex Williamson     .valid = {
239d35e428cSAlex Williamson         .min_access_size = 4,
240191f90cbSMichael S. Tsirkin         .max_access_size = 8,
241191f90cbSMichael S. Tsirkin     },
242191f90cbSMichael S. Tsirkin     .impl = {
243d35e428cSAlex Williamson         .max_access_size = 4,
244d35e428cSAlex Williamson     },
245d35e428cSAlex Williamson };
246d35e428cSAlex Williamson 
247a8170e5eSAvi Kivity static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr,
248d35e428cSAlex Williamson                                    unsigned size)
249d35e428cSAlex Williamson {
250d35e428cSAlex Williamson     PCIDevice *dev = opaque;
251bbef882cSMichael S. Tsirkin     if (dev->msix_vector_poll_notifier) {
252bbef882cSMichael S. Tsirkin         unsigned vector_start = addr * 8;
253bbef882cSMichael S. Tsirkin         unsigned vector_end = MIN(addr + size * 8, dev->msix_entries_nr);
254bbef882cSMichael S. Tsirkin         dev->msix_vector_poll_notifier(dev, vector_start, vector_end);
255bbef882cSMichael S. Tsirkin     }
256d35e428cSAlex Williamson 
257d35e428cSAlex Williamson     return pci_get_long(dev->msix_pba + addr);
258d35e428cSAlex Williamson }
259d35e428cSAlex Williamson 
26043b11a91SMarc-André Lureau static void msix_pba_mmio_write(void *opaque, hwaddr addr,
26143b11a91SMarc-André Lureau                                 uint64_t val, unsigned size)
26243b11a91SMarc-André Lureau {
26343b11a91SMarc-André Lureau }
26443b11a91SMarc-André Lureau 
265d35e428cSAlex Williamson static const MemoryRegionOps msix_pba_mmio_ops = {
266d35e428cSAlex Williamson     .read = msix_pba_mmio_read,
26743b11a91SMarc-André Lureau     .write = msix_pba_mmio_write,
26868d1e1f5SAlexander Graf     .endianness = DEVICE_LITTLE_ENDIAN,
26995524ae8SAvi Kivity     .valid = {
27095524ae8SAvi Kivity         .min_access_size = 4,
271191f90cbSMichael S. Tsirkin         .max_access_size = 8,
272191f90cbSMichael S. Tsirkin     },
273191f90cbSMichael S. Tsirkin     .impl = {
27495524ae8SAvi Kivity         .max_access_size = 4,
27595524ae8SAvi Kivity     },
27602eb84d0SMichael S. Tsirkin };
27702eb84d0SMichael S. Tsirkin 
278ae1be0bbSMichael S. Tsirkin static void msix_mask_all(struct PCIDevice *dev, unsigned nentries)
279ae1be0bbSMichael S. Tsirkin {
280ae1be0bbSMichael S. Tsirkin     int vector;
2815b5f1330SJan Kiszka 
282ae1be0bbSMichael S. Tsirkin     for (vector = 0; vector < nentries; ++vector) {
28301731cfbSJan Kiszka         unsigned offset =
28401731cfbSJan Kiszka             vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
2855b5f1330SJan Kiszka         bool was_masked = msix_is_masked(dev, vector);
2865b5f1330SJan Kiszka 
287d35e428cSAlex Williamson         dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
2885b5f1330SJan Kiszka         msix_handle_mask_update(dev, vector, was_masked);
289ae1be0bbSMichael S. Tsirkin     }
290ae1be0bbSMichael S. Tsirkin }
291ae1be0bbSMichael S. Tsirkin 
292ee640c62SCao jin /*
293ee640c62SCao jin  * Make PCI device @dev MSI-X capable
294ee640c62SCao jin  * @nentries is the max number of MSI-X vectors that the device support.
295ee640c62SCao jin  * @table_bar is the MemoryRegion that MSI-X table structure resides.
296ee640c62SCao jin  * @table_bar_nr is number of base address register corresponding to @table_bar.
297ee640c62SCao jin  * @table_offset indicates the offset that the MSI-X table structure starts with
298ee640c62SCao jin  * in @table_bar.
299ee640c62SCao jin  * @pba_bar is the MemoryRegion that the Pending Bit Array structure resides.
300ee640c62SCao jin  * @pba_bar_nr is number of base address register corresponding to @pba_bar.
301ee640c62SCao jin  * @pba_offset indicates the offset that the Pending Bit Array structure
302ee640c62SCao jin  * starts with in @pba_bar.
303ee640c62SCao jin  * Non-zero @cap_pos puts capability MSI-X at that offset in PCI config space.
304ee640c62SCao jin  * @errp is for returning errors.
305ee640c62SCao jin  *
306ee640c62SCao jin  * Return 0 on success; set @errp and return -errno on error:
307ee640c62SCao jin  * -ENOTSUP means lacking msi support for a msi-capable platform.
308ee640c62SCao jin  * -EINVAL means capability overlap, happens when @cap_pos is non-zero,
309ee640c62SCao jin  * also means a programming error, except device assignment, which can check
310ee640c62SCao jin  * if a real HW is broken.
311ee640c62SCao jin  */
31202eb84d0SMichael S. Tsirkin int msix_init(struct PCIDevice *dev, unsigned short nentries,
3135a2c2029SAlex Williamson               MemoryRegion *table_bar, uint8_t table_bar_nr,
3145a2c2029SAlex Williamson               unsigned table_offset, MemoryRegion *pba_bar,
315ee640c62SCao jin               uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos,
316ee640c62SCao jin               Error **errp)
31702eb84d0SMichael S. Tsirkin {
3185a2c2029SAlex Williamson     int cap;
319d35e428cSAlex Williamson     unsigned table_size, pba_size;
3205a2c2029SAlex Williamson     uint8_t *config;
32102eb84d0SMichael S. Tsirkin 
32260ba3cc2SJan Kiszka     /* Nothing to do if MSI is not supported by interrupt controller */
323226419d6SMichael S. Tsirkin     if (!msi_nonbroken) {
324ee640c62SCao jin         error_setg(errp, "MSI-X is not supported by interrupt controller");
32560ba3cc2SJan Kiszka         return -ENOTSUP;
32660ba3cc2SJan Kiszka     }
3275a2c2029SAlex Williamson 
3285a2c2029SAlex Williamson     if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) {
329ee640c62SCao jin         error_setg(errp, "The number of MSI-X vectors is invalid");
33002eb84d0SMichael S. Tsirkin         return -EINVAL;
3315a2c2029SAlex Williamson     }
33202eb84d0SMichael S. Tsirkin 
333d35e428cSAlex Williamson     table_size = nentries * PCI_MSIX_ENTRY_SIZE;
334d35e428cSAlex Williamson     pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
335d35e428cSAlex Williamson 
3365a2c2029SAlex Williamson     /* Sanity test: table & pba don't overlap, fit within BARs, min aligned */
3375a2c2029SAlex Williamson     if ((table_bar_nr == pba_bar_nr &&
3385a2c2029SAlex Williamson          ranges_overlap(table_offset, table_size, pba_offset, pba_size)) ||
3395a2c2029SAlex Williamson         table_offset + table_size > memory_region_size(table_bar) ||
3405a2c2029SAlex Williamson         pba_offset + pba_size > memory_region_size(pba_bar) ||
3415a2c2029SAlex Williamson         (table_offset | pba_offset) & PCI_MSIX_FLAGS_BIRMASK) {
342ee640c62SCao jin         error_setg(errp, "table & pba overlap, or they don't fit in BARs,"
343ee640c62SCao jin                    " or don't align");
3445a2c2029SAlex Williamson         return -EINVAL;
3455a2c2029SAlex Williamson     }
3465a2c2029SAlex Williamson 
34727841278SMao Zhongyi     cap = pci_add_capability(dev, PCI_CAP_ID_MSIX,
348ee640c62SCao jin                               cap_pos, MSIX_CAP_LENGTH, errp);
3495a2c2029SAlex Williamson     if (cap < 0) {
3505a2c2029SAlex Williamson         return cap;
3515a2c2029SAlex Williamson     }
3525a2c2029SAlex Williamson 
3535a2c2029SAlex Williamson     dev->msix_cap = cap;
3545a2c2029SAlex Williamson     dev->cap_present |= QEMU_PCI_CAP_MSIX;
3555a2c2029SAlex Williamson     config = dev->config + cap;
3565a2c2029SAlex Williamson 
3575a2c2029SAlex Williamson     pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1);
3585a2c2029SAlex Williamson     dev->msix_entries_nr = nentries;
3595a2c2029SAlex Williamson     dev->msix_function_masked = true;
3605a2c2029SAlex Williamson 
3615a2c2029SAlex Williamson     pci_set_long(config + PCI_MSIX_TABLE, table_offset | table_bar_nr);
3625a2c2029SAlex Williamson     pci_set_long(config + PCI_MSIX_PBA, pba_offset | pba_bar_nr);
3635a2c2029SAlex Williamson 
3645a2c2029SAlex Williamson     /* Make flags bit writable. */
3655a2c2029SAlex Williamson     dev->wmask[cap + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK |
3665a2c2029SAlex Williamson                                              MSIX_MASKALL_MASK;
36702eb84d0SMichael S. Tsirkin 
368d35e428cSAlex Williamson     dev->msix_table = g_malloc0(table_size);
369d35e428cSAlex Williamson     dev->msix_pba = g_malloc0(pba_size);
3705a2c2029SAlex Williamson     dev->msix_entry_used = g_malloc0(nentries * sizeof *dev->msix_entry_used);
3715a2c2029SAlex Williamson 
372ae1be0bbSMichael S. Tsirkin     msix_mask_all(dev, nentries);
37302eb84d0SMichael S. Tsirkin 
37440c5dce9SPaolo Bonzini     memory_region_init_io(&dev->msix_table_mmio, OBJECT(dev), &msix_table_mmio_ops, dev,
375d35e428cSAlex Williamson                           "msix-table", table_size);
3765a2c2029SAlex Williamson     memory_region_add_subregion(table_bar, table_offset, &dev->msix_table_mmio);
37740c5dce9SPaolo Bonzini     memory_region_init_io(&dev->msix_pba_mmio, OBJECT(dev), &msix_pba_mmio_ops, dev,
378d35e428cSAlex Williamson                           "msix-pba", pba_size);
3795a2c2029SAlex Williamson     memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio);
38002eb84d0SMichael S. Tsirkin 
38108cf3dc6SJagannathan Raman     dev->msix_prepare_message = msix_prepare_message;
38208cf3dc6SJagannathan Raman 
38302eb84d0SMichael S. Tsirkin     return 0;
38402eb84d0SMichael S. Tsirkin }
38502eb84d0SMichael S. Tsirkin 
38653f94925SAlex Williamson int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
387ee640c62SCao jin                             uint8_t bar_nr, Error **errp)
38853f94925SAlex Williamson {
38953f94925SAlex Williamson     int ret;
39053f94925SAlex Williamson     char *name;
391a0ccd212SJason Wang     uint32_t bar_size = 4096;
392a0ccd212SJason Wang     uint32_t bar_pba_offset = bar_size / 2;
39317323e8bSDongli Zhang     uint32_t bar_pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
39453f94925SAlex Williamson 
39553f94925SAlex Williamson     /*
39653f94925SAlex Williamson      * Migration compatibility dictates that this remains a 4k
39753f94925SAlex Williamson      * BAR with the vector table in the lower half and PBA in
398a0ccd212SJason Wang      * the upper half for nentries which is lower or equal to 128.
399a0ccd212SJason Wang      * No need to care about using more than 65 entries for legacy
400a0ccd212SJason Wang      * machine types who has at most 64 queues.
40153f94925SAlex Williamson      */
402a0ccd212SJason Wang     if (nentries * PCI_MSIX_ENTRY_SIZE > bar_pba_offset) {
403a0ccd212SJason Wang         bar_pba_offset = nentries * PCI_MSIX_ENTRY_SIZE;
404a0ccd212SJason Wang     }
40553f94925SAlex Williamson 
406a0ccd212SJason Wang     if (bar_pba_offset + bar_pba_size > 4096) {
407a0ccd212SJason Wang         bar_size = bar_pba_offset + bar_pba_size;
408a0ccd212SJason Wang     }
409a0ccd212SJason Wang 
4109bff5d81SPeter Maydell     bar_size = pow2ceil(bar_size);
41153f94925SAlex Williamson 
4125f893b4eSGerd Hoffmann     name = g_strdup_printf("%s-msix", dev->name);
413a0ccd212SJason Wang     memory_region_init(&dev->msix_exclusive_bar, OBJECT(dev), name, bar_size);
4145f893b4eSGerd Hoffmann     g_free(name);
41553f94925SAlex Williamson 
41653f94925SAlex Williamson     ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr,
417a0ccd212SJason Wang                     0, &dev->msix_exclusive_bar,
418a0ccd212SJason Wang                     bar_nr, bar_pba_offset,
419ee640c62SCao jin                     0, errp);
42053f94925SAlex Williamson     if (ret) {
42153f94925SAlex Williamson         return ret;
42253f94925SAlex Williamson     }
42353f94925SAlex Williamson 
42453f94925SAlex Williamson     pci_register_bar(dev, bar_nr, PCI_BASE_ADDRESS_SPACE_MEMORY,
42553f94925SAlex Williamson                      &dev->msix_exclusive_bar);
42653f94925SAlex Williamson 
42753f94925SAlex Williamson     return 0;
42853f94925SAlex Williamson }
42953f94925SAlex Williamson 
43098304c84SMichael S. Tsirkin static void msix_free_irq_entries(PCIDevice *dev)
43198304c84SMichael S. Tsirkin {
43298304c84SMichael S. Tsirkin     int vector;
43398304c84SMichael S. Tsirkin 
43498304c84SMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
43598304c84SMichael S. Tsirkin         dev->msix_entry_used[vector] = 0;
43698304c84SMichael S. Tsirkin         msix_clr_pending(dev, vector);
43798304c84SMichael S. Tsirkin     }
43898304c84SMichael S. Tsirkin }
43998304c84SMichael S. Tsirkin 
4403cac001eSMichael S. Tsirkin static void msix_clear_all_vectors(PCIDevice *dev)
4413cac001eSMichael S. Tsirkin {
4423cac001eSMichael S. Tsirkin     int vector;
4433cac001eSMichael S. Tsirkin 
4443cac001eSMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
4453cac001eSMichael S. Tsirkin         msix_clr_pending(dev, vector);
4463cac001eSMichael S. Tsirkin     }
4473cac001eSMichael S. Tsirkin }
4483cac001eSMichael S. Tsirkin 
44902eb84d0SMichael S. Tsirkin /* Clean up resources for the device. */
450572992eeSAlex Williamson void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar)
45102eb84d0SMichael S. Tsirkin {
45244701ab7SJan Kiszka     if (!msix_present(dev)) {
453572992eeSAlex Williamson         return;
45444701ab7SJan Kiszka     }
45502eb84d0SMichael S. Tsirkin     pci_del_capability(dev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH);
45602eb84d0SMichael S. Tsirkin     dev->msix_cap = 0;
45702eb84d0SMichael S. Tsirkin     msix_free_irq_entries(dev);
45802eb84d0SMichael S. Tsirkin     dev->msix_entries_nr = 0;
4595a2c2029SAlex Williamson     memory_region_del_subregion(pba_bar, &dev->msix_pba_mmio);
460d35e428cSAlex Williamson     g_free(dev->msix_pba);
461d35e428cSAlex Williamson     dev->msix_pba = NULL;
4625a2c2029SAlex Williamson     memory_region_del_subregion(table_bar, &dev->msix_table_mmio);
463d35e428cSAlex Williamson     g_free(dev->msix_table);
464d35e428cSAlex Williamson     dev->msix_table = NULL;
4657267c094SAnthony Liguori     g_free(dev->msix_entry_used);
46602eb84d0SMichael S. Tsirkin     dev->msix_entry_used = NULL;
46702eb84d0SMichael S. Tsirkin     dev->cap_present &= ~QEMU_PCI_CAP_MSIX;
46808cf3dc6SJagannathan Raman     dev->msix_prepare_message = NULL;
46902eb84d0SMichael S. Tsirkin }
47002eb84d0SMichael S. Tsirkin 
47153f94925SAlex Williamson void msix_uninit_exclusive_bar(PCIDevice *dev)
47253f94925SAlex Williamson {
47353f94925SAlex Williamson     if (msix_present(dev)) {
4745a2c2029SAlex Williamson         msix_uninit(dev, &dev->msix_exclusive_bar, &dev->msix_exclusive_bar);
47553f94925SAlex Williamson     }
47653f94925SAlex Williamson }
47753f94925SAlex Williamson 
47802eb84d0SMichael S. Tsirkin void msix_save(PCIDevice *dev, QEMUFile *f)
47902eb84d0SMichael S. Tsirkin {
4809a3e12c8SMichael S. Tsirkin     unsigned n = dev->msix_entries_nr;
4819a3e12c8SMichael S. Tsirkin 
48244701ab7SJan Kiszka     if (!msix_present(dev)) {
4839a3e12c8SMichael S. Tsirkin         return;
48472755a70SMichael S. Tsirkin     }
4859a3e12c8SMichael S. Tsirkin 
486d35e428cSAlex Williamson     qemu_put_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
4870ef1efcfSMarc-André Lureau     qemu_put_buffer(f, dev->msix_pba, DIV_ROUND_UP(n, 8));
48802eb84d0SMichael S. Tsirkin }
48902eb84d0SMichael S. Tsirkin 
49002eb84d0SMichael S. Tsirkin /* Should be called after restoring the config space. */
49102eb84d0SMichael S. Tsirkin void msix_load(PCIDevice *dev, QEMUFile *f)
49202eb84d0SMichael S. Tsirkin {
49302eb84d0SMichael S. Tsirkin     unsigned n = dev->msix_entries_nr;
4942cdfe53cSJan Kiszka     unsigned int vector;
49502eb84d0SMichael S. Tsirkin 
49644701ab7SJan Kiszka     if (!msix_present(dev)) {
49702eb84d0SMichael S. Tsirkin         return;
49898846d73SBlue Swirl     }
49902eb84d0SMichael S. Tsirkin 
5003cac001eSMichael S. Tsirkin     msix_clear_all_vectors(dev);
501d35e428cSAlex Williamson     qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
5020ef1efcfSMarc-André Lureau     qemu_get_buffer(f, dev->msix_pba, DIV_ROUND_UP(n, 8));
50350322249SMichael S. Tsirkin     msix_update_function_masked(dev);
5042cdfe53cSJan Kiszka 
5052cdfe53cSJan Kiszka     for (vector = 0; vector < n; vector++) {
5062cdfe53cSJan Kiszka         msix_handle_mask_update(dev, vector, true);
5072cdfe53cSJan Kiszka     }
50802eb84d0SMichael S. Tsirkin }
50902eb84d0SMichael S. Tsirkin 
51002eb84d0SMichael S. Tsirkin /* Does device support MSI-X? */
51102eb84d0SMichael S. Tsirkin int msix_present(PCIDevice *dev)
51202eb84d0SMichael S. Tsirkin {
51302eb84d0SMichael S. Tsirkin     return dev->cap_present & QEMU_PCI_CAP_MSIX;
51402eb84d0SMichael S. Tsirkin }
51502eb84d0SMichael S. Tsirkin 
51602eb84d0SMichael S. Tsirkin /* Is MSI-X enabled? */
51702eb84d0SMichael S. Tsirkin int msix_enabled(PCIDevice *dev)
51802eb84d0SMichael S. Tsirkin {
51902eb84d0SMichael S. Tsirkin     return (dev->cap_present & QEMU_PCI_CAP_MSIX) &&
5202760952bSMichael S. Tsirkin         (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
52102eb84d0SMichael S. Tsirkin          MSIX_ENABLE_MASK);
52202eb84d0SMichael S. Tsirkin }
52302eb84d0SMichael S. Tsirkin 
52402eb84d0SMichael S. Tsirkin /* Send an MSI-X message */
52502eb84d0SMichael S. Tsirkin void msix_notify(PCIDevice *dev, unsigned vector)
52602eb84d0SMichael S. Tsirkin {
527bc4caf49SJan Kiszka     MSIMessage msg;
52802eb84d0SMichael S. Tsirkin 
52915377f6eSAkihiko Odaki     assert(vector < dev->msix_entries_nr);
53015377f6eSAkihiko Odaki 
53115377f6eSAkihiko Odaki     if (!dev->msix_entry_used[vector]) {
53202eb84d0SMichael S. Tsirkin         return;
53393482436SCao jin     }
53493482436SCao jin 
53502eb84d0SMichael S. Tsirkin     if (msix_is_masked(dev, vector)) {
53602eb84d0SMichael S. Tsirkin         msix_set_pending(dev, vector);
53702eb84d0SMichael S. Tsirkin         return;
53802eb84d0SMichael S. Tsirkin     }
53902eb84d0SMichael S. Tsirkin 
540bc4caf49SJan Kiszka     msg = msix_get_message(dev, vector);
541bc4caf49SJan Kiszka 
54238d40ff1SPavel Fedin     msi_send_message(dev, msg);
54302eb84d0SMichael S. Tsirkin }
54402eb84d0SMichael S. Tsirkin 
54502eb84d0SMichael S. Tsirkin void msix_reset(PCIDevice *dev)
54602eb84d0SMichael S. Tsirkin {
54744701ab7SJan Kiszka     if (!msix_present(dev)) {
54802eb84d0SMichael S. Tsirkin         return;
54944701ab7SJan Kiszka     }
5503cac001eSMichael S. Tsirkin     msix_clear_all_vectors(dev);
5512760952bSMichael S. Tsirkin     dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &=
5522760952bSMichael S. Tsirkin             ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET];
553d35e428cSAlex Williamson     memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
554d35e428cSAlex Williamson     memset(dev->msix_pba, 0, QEMU_ALIGN_UP(dev->msix_entries_nr, 64) / 8);
555ae1be0bbSMichael S. Tsirkin     msix_mask_all(dev, dev->msix_entries_nr);
55602eb84d0SMichael S. Tsirkin }
55702eb84d0SMichael S. Tsirkin 
55802eb84d0SMichael S. Tsirkin /* PCI spec suggests that devices make it possible for software to configure
55902eb84d0SMichael S. Tsirkin  * less vectors than supported by the device, but does not specify a standard
56002eb84d0SMichael S. Tsirkin  * mechanism for devices to do so.
56102eb84d0SMichael S. Tsirkin  *
56202eb84d0SMichael S. Tsirkin  * We support this by asking devices to declare vectors software is going to
56302eb84d0SMichael S. Tsirkin  * actually use, and checking this on the notification path. Devices that
56402eb84d0SMichael S. Tsirkin  * don't want to follow the spec suggestion can declare all vectors as used. */
56502eb84d0SMichael S. Tsirkin 
56602eb84d0SMichael S. Tsirkin /* Mark vector as used. */
56715377f6eSAkihiko Odaki void msix_vector_use(PCIDevice *dev, unsigned vector)
56802eb84d0SMichael S. Tsirkin {
56915377f6eSAkihiko Odaki     assert(vector < dev->msix_entries_nr);
57002eb84d0SMichael S. Tsirkin     dev->msix_entry_used[vector]++;
57102eb84d0SMichael S. Tsirkin }
57202eb84d0SMichael S. Tsirkin 
57302eb84d0SMichael S. Tsirkin /* Mark vector as unused. */
57402eb84d0SMichael S. Tsirkin void msix_vector_unuse(PCIDevice *dev, unsigned vector)
57502eb84d0SMichael S. Tsirkin {
57615377f6eSAkihiko Odaki     assert(vector < dev->msix_entries_nr);
57715377f6eSAkihiko Odaki     if (!dev->msix_entry_used[vector]) {
57898304c84SMichael S. Tsirkin         return;
57998304c84SMichael S. Tsirkin     }
58098304c84SMichael S. Tsirkin     if (--dev->msix_entry_used[vector]) {
58198304c84SMichael S. Tsirkin         return;
58298304c84SMichael S. Tsirkin     }
58398304c84SMichael S. Tsirkin     msix_clr_pending(dev, vector);
58402eb84d0SMichael S. Tsirkin }
585b5f28bcaSMichael S. Tsirkin 
586b5f28bcaSMichael S. Tsirkin void msix_unuse_all_vectors(PCIDevice *dev)
587b5f28bcaSMichael S. Tsirkin {
58844701ab7SJan Kiszka     if (!msix_present(dev)) {
589b5f28bcaSMichael S. Tsirkin         return;
59044701ab7SJan Kiszka     }
591b5f28bcaSMichael S. Tsirkin     msix_free_irq_entries(dev);
592b5f28bcaSMichael S. Tsirkin }
5932cdfe53cSJan Kiszka 
594cb697aaaSJan Kiszka unsigned int msix_nr_vectors_allocated(const PCIDevice *dev)
595cb697aaaSJan Kiszka {
596cb697aaaSJan Kiszka     return dev->msix_entries_nr;
597cb697aaaSJan Kiszka }
598cb697aaaSJan Kiszka 
5992cdfe53cSJan Kiszka static int msix_set_notifier_for_vector(PCIDevice *dev, unsigned int vector)
6002cdfe53cSJan Kiszka {
6012cdfe53cSJan Kiszka     MSIMessage msg;
6022cdfe53cSJan Kiszka 
6032cdfe53cSJan Kiszka     if (msix_is_masked(dev, vector)) {
6042cdfe53cSJan Kiszka         return 0;
6052cdfe53cSJan Kiszka     }
6062cdfe53cSJan Kiszka     msg = msix_get_message(dev, vector);
6072cdfe53cSJan Kiszka     return dev->msix_vector_use_notifier(dev, vector, msg);
6082cdfe53cSJan Kiszka }
6092cdfe53cSJan Kiszka 
6102cdfe53cSJan Kiszka static void msix_unset_notifier_for_vector(PCIDevice *dev, unsigned int vector)
6112cdfe53cSJan Kiszka {
6122cdfe53cSJan Kiszka     if (msix_is_masked(dev, vector)) {
6132cdfe53cSJan Kiszka         return;
6142cdfe53cSJan Kiszka     }
6152cdfe53cSJan Kiszka     dev->msix_vector_release_notifier(dev, vector);
6162cdfe53cSJan Kiszka }
6172cdfe53cSJan Kiszka 
6182cdfe53cSJan Kiszka int msix_set_vector_notifiers(PCIDevice *dev,
6192cdfe53cSJan Kiszka                               MSIVectorUseNotifier use_notifier,
620bbef882cSMichael S. Tsirkin                               MSIVectorReleaseNotifier release_notifier,
621bbef882cSMichael S. Tsirkin                               MSIVectorPollNotifier poll_notifier)
6222cdfe53cSJan Kiszka {
6232cdfe53cSJan Kiszka     int vector, ret;
6242cdfe53cSJan Kiszka 
6252cdfe53cSJan Kiszka     assert(use_notifier && release_notifier);
6262cdfe53cSJan Kiszka 
6272cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = use_notifier;
6282cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = release_notifier;
629bbef882cSMichael S. Tsirkin     dev->msix_vector_poll_notifier = poll_notifier;
6302cdfe53cSJan Kiszka 
6312cdfe53cSJan Kiszka     if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
6322cdfe53cSJan Kiszka         (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) {
6332cdfe53cSJan Kiszka         for (vector = 0; vector < dev->msix_entries_nr; vector++) {
6342cdfe53cSJan Kiszka             ret = msix_set_notifier_for_vector(dev, vector);
6352cdfe53cSJan Kiszka             if (ret < 0) {
6362cdfe53cSJan Kiszka                 goto undo;
6372cdfe53cSJan Kiszka             }
6382cdfe53cSJan Kiszka         }
6392cdfe53cSJan Kiszka     }
640bbef882cSMichael S. Tsirkin     if (dev->msix_vector_poll_notifier) {
641bbef882cSMichael S. Tsirkin         dev->msix_vector_poll_notifier(dev, 0, dev->msix_entries_nr);
642bbef882cSMichael S. Tsirkin     }
6432cdfe53cSJan Kiszka     return 0;
6442cdfe53cSJan Kiszka 
6452cdfe53cSJan Kiszka undo:
6462cdfe53cSJan Kiszka     while (--vector >= 0) {
6472cdfe53cSJan Kiszka         msix_unset_notifier_for_vector(dev, vector);
6482cdfe53cSJan Kiszka     }
6492cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = NULL;
6502cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = NULL;
651*2d37fe9eSRobert Hoo     dev->msix_vector_poll_notifier = NULL;
6522cdfe53cSJan Kiszka     return ret;
6532cdfe53cSJan Kiszka }
6542cdfe53cSJan Kiszka 
6552cdfe53cSJan Kiszka void msix_unset_vector_notifiers(PCIDevice *dev)
6562cdfe53cSJan Kiszka {
6572cdfe53cSJan Kiszka     int vector;
6582cdfe53cSJan Kiszka 
6592cdfe53cSJan Kiszka     assert(dev->msix_vector_use_notifier &&
6602cdfe53cSJan Kiszka            dev->msix_vector_release_notifier);
6612cdfe53cSJan Kiszka 
6622cdfe53cSJan Kiszka     if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
6632cdfe53cSJan Kiszka         (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) {
6642cdfe53cSJan Kiszka         for (vector = 0; vector < dev->msix_entries_nr; vector++) {
6652cdfe53cSJan Kiszka             msix_unset_notifier_for_vector(dev, vector);
6662cdfe53cSJan Kiszka         }
6672cdfe53cSJan Kiszka     }
6682cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = NULL;
6692cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = NULL;
670bbef882cSMichael S. Tsirkin     dev->msix_vector_poll_notifier = NULL;
6712cdfe53cSJan Kiszka }
672340b50c7SGerd Hoffmann 
6732c21ee76SJianjun Duan static int put_msix_state(QEMUFile *f, void *pv, size_t size,
6743ddba9a9SMarkus Armbruster                           const VMStateField *field, JSONWriter *vmdesc)
675340b50c7SGerd Hoffmann {
676340b50c7SGerd Hoffmann     msix_save(pv, f);
6772c21ee76SJianjun Duan 
6782c21ee76SJianjun Duan     return 0;
679340b50c7SGerd Hoffmann }
680340b50c7SGerd Hoffmann 
6812c21ee76SJianjun Duan static int get_msix_state(QEMUFile *f, void *pv, size_t size,
68203fee66fSMarc-André Lureau                           const VMStateField *field)
683340b50c7SGerd Hoffmann {
684340b50c7SGerd Hoffmann     msix_load(pv, f);
685340b50c7SGerd Hoffmann     return 0;
686340b50c7SGerd Hoffmann }
687340b50c7SGerd Hoffmann 
688340b50c7SGerd Hoffmann static VMStateInfo vmstate_info_msix = {
689340b50c7SGerd Hoffmann     .name = "msix state",
690340b50c7SGerd Hoffmann     .get  = get_msix_state,
691340b50c7SGerd Hoffmann     .put  = put_msix_state,
692340b50c7SGerd Hoffmann };
693340b50c7SGerd Hoffmann 
694340b50c7SGerd Hoffmann const VMStateDescription vmstate_msix = {
695340b50c7SGerd Hoffmann     .name = "msix",
696340b50c7SGerd Hoffmann     .fields = (VMStateField[]) {
697340b50c7SGerd Hoffmann         {
698340b50c7SGerd Hoffmann             .name         = "msix",
699340b50c7SGerd Hoffmann             .version_id   = 0,
700340b50c7SGerd Hoffmann             .field_exists = NULL,
701340b50c7SGerd Hoffmann             .size         = 0,   /* ouch */
702340b50c7SGerd Hoffmann             .info         = &vmstate_info_msix,
703340b50c7SGerd Hoffmann             .flags        = VMS_SINGLE,
704340b50c7SGerd Hoffmann             .offset       = 0,
705340b50c7SGerd Hoffmann         },
706340b50c7SGerd Hoffmann         VMSTATE_END_OF_LIST()
707340b50c7SGerd Hoffmann     }
708340b50c7SGerd Hoffmann };
709