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"
18*44ed44aeSNicholas Piggin #include "qemu/log.h"
19c759b24fSMichael S. Tsirkin #include "hw/pci/msi.h"
20c759b24fSMichael S. Tsirkin #include "hw/pci/msix.h"
21c759b24fSMichael S. Tsirkin #include "hw/pci/pci.h"
22428c3eceSStefano Stabellini #include "hw/xen/xen.h"
2332cad1ffSPhilippe Mathieu-Daudé #include "system/xen.h"
24ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h"
25d6454270SMarkus Armbruster #include "migration/vmstate.h"
261de7afc9SPaolo Bonzini #include "qemu/range.h"
27ee640c62SCao jin #include "qapi/error.h"
28993b1f4bSPeter Xu #include "trace.h"
2902eb84d0SMichael S. Tsirkin
306096cf78SDavid Woodhouse #include "hw/i386/kvm/xen_evtchn.h"
316096cf78SDavid Woodhouse
322760952bSMichael S. Tsirkin /* MSI enable bit and maskall bit are in byte 1 in FLAGS register */
332760952bSMichael S. Tsirkin #define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1)
3402eb84d0SMichael S. Tsirkin #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8)
355b5cb086SMichael S. Tsirkin #define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8)
3602eb84d0SMichael S. Tsirkin
msix_prepare_message(PCIDevice * dev,unsigned vector)3708cf3dc6SJagannathan Raman static MSIMessage msix_prepare_message(PCIDevice *dev, unsigned vector)
38bc4caf49SJan Kiszka {
39d35e428cSAlex Williamson uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE;
40bc4caf49SJan Kiszka MSIMessage msg;
41bc4caf49SJan Kiszka
42bc4caf49SJan Kiszka msg.address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR);
43bc4caf49SJan Kiszka msg.data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA);
44bc4caf49SJan Kiszka return msg;
45bc4caf49SJan Kiszka }
4602eb84d0SMichael S. Tsirkin
msix_get_message(PCIDevice * dev,unsigned vector)4708cf3dc6SJagannathan Raman MSIMessage msix_get_message(PCIDevice *dev, unsigned vector)
4808cf3dc6SJagannathan Raman {
4908cf3dc6SJagannathan Raman return dev->msix_prepare_message(dev, vector);
5008cf3dc6SJagannathan Raman }
5108cf3dc6SJagannathan Raman
52932d4a42SAlexey Kardashevskiy /*
53932d4a42SAlexey Kardashevskiy * Special API for POWER to configure the vectors through
54932d4a42SAlexey Kardashevskiy * a side channel. Should never be used by devices.
55932d4a42SAlexey Kardashevskiy */
msix_set_message(PCIDevice * dev,int vector,struct MSIMessage msg)56932d4a42SAlexey Kardashevskiy void msix_set_message(PCIDevice *dev, int vector, struct MSIMessage msg)
57932d4a42SAlexey Kardashevskiy {
58932d4a42SAlexey Kardashevskiy uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE;
59932d4a42SAlexey Kardashevskiy
60932d4a42SAlexey Kardashevskiy pci_set_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR, msg.address);
61932d4a42SAlexey Kardashevskiy pci_set_long(table_entry + PCI_MSIX_ENTRY_DATA, msg.data);
62932d4a42SAlexey Kardashevskiy table_entry[PCI_MSIX_ENTRY_VECTOR_CTRL] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
63932d4a42SAlexey Kardashevskiy }
64932d4a42SAlexey Kardashevskiy
msix_pending_mask(int vector)6502eb84d0SMichael S. Tsirkin static uint8_t msix_pending_mask(int vector)
6602eb84d0SMichael S. Tsirkin {
6702eb84d0SMichael S. Tsirkin return 1 << (vector % 8);
6802eb84d0SMichael S. Tsirkin }
6902eb84d0SMichael S. Tsirkin
msix_pending_byte(PCIDevice * dev,int vector)7002eb84d0SMichael S. Tsirkin static uint8_t *msix_pending_byte(PCIDevice *dev, int vector)
7102eb84d0SMichael S. Tsirkin {
72d35e428cSAlex Williamson return dev->msix_pba + vector / 8;
7302eb84d0SMichael S. Tsirkin }
7402eb84d0SMichael S. Tsirkin
msix_is_pending(PCIDevice * dev,int vector)7502eb84d0SMichael S. Tsirkin static int msix_is_pending(PCIDevice *dev, int vector)
7602eb84d0SMichael S. Tsirkin {
7702eb84d0SMichael S. Tsirkin return *msix_pending_byte(dev, vector) & msix_pending_mask(vector);
7802eb84d0SMichael S. Tsirkin }
7902eb84d0SMichael S. Tsirkin
msix_set_pending(PCIDevice * dev,unsigned int vector)8070f8ee39SMichael S. Tsirkin void msix_set_pending(PCIDevice *dev, unsigned int vector)
8102eb84d0SMichael S. Tsirkin {
8202eb84d0SMichael S. Tsirkin *msix_pending_byte(dev, vector) |= msix_pending_mask(vector);
8302eb84d0SMichael S. Tsirkin }
8402eb84d0SMichael S. Tsirkin
msix_clr_pending(PCIDevice * dev,int vector)853bdfaabbSDmitry Fleytman void msix_clr_pending(PCIDevice *dev, int vector)
8602eb84d0SMichael S. Tsirkin {
8702eb84d0SMichael S. Tsirkin *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector);
8802eb84d0SMichael S. Tsirkin }
8902eb84d0SMichael S. Tsirkin
msix_vector_masked(PCIDevice * dev,unsigned int vector,bool fmask)9070f8ee39SMichael S. Tsirkin static bool msix_vector_masked(PCIDevice *dev, unsigned int vector, bool fmask)
9102eb84d0SMichael S. Tsirkin {
92428c3eceSStefano Stabellini unsigned offset = vector * PCI_MSIX_ENTRY_SIZE;
93e1e4bf22SMichael S. Tsirkin uint8_t *data = &dev->msix_table[offset + PCI_MSIX_ENTRY_DATA];
94428c3eceSStefano Stabellini /* MSIs on Xen can be remapped into pirqs. In those cases, masking
95428c3eceSStefano Stabellini * and unmasking go through the PV evtchn path. */
96e1e4bf22SMichael S. Tsirkin if (xen_enabled() && xen_is_pirq_msi(pci_get_long(data))) {
97428c3eceSStefano Stabellini return false;
98428c3eceSStefano Stabellini }
99428c3eceSStefano Stabellini return fmask || dev->msix_table[offset + PCI_MSIX_ENTRY_VECTOR_CTRL] &
100428c3eceSStefano Stabellini PCI_MSIX_ENTRY_CTRL_MASKBIT;
1015b5cb086SMichael S. Tsirkin }
1025b5cb086SMichael S. Tsirkin
msix_is_masked(PCIDevice * dev,unsigned int vector)10370f8ee39SMichael S. Tsirkin bool msix_is_masked(PCIDevice *dev, unsigned int vector)
1045b5cb086SMichael S. Tsirkin {
105ae392c41SMichael S. Tsirkin return msix_vector_masked(dev, vector, dev->msix_function_masked);
106ae392c41SMichael S. Tsirkin }
107ae392c41SMichael S. Tsirkin
msix_fire_vector_notifier(PCIDevice * dev,unsigned int vector,bool is_masked)1082cdfe53cSJan Kiszka static void msix_fire_vector_notifier(PCIDevice *dev,
1092cdfe53cSJan Kiszka unsigned int vector, bool is_masked)
1102cdfe53cSJan Kiszka {
1112cdfe53cSJan Kiszka MSIMessage msg;
1122cdfe53cSJan Kiszka int ret;
1132cdfe53cSJan Kiszka
1142cdfe53cSJan Kiszka if (!dev->msix_vector_use_notifier) {
1152cdfe53cSJan Kiszka return;
1162cdfe53cSJan Kiszka }
1172cdfe53cSJan Kiszka if (is_masked) {
1182cdfe53cSJan Kiszka dev->msix_vector_release_notifier(dev, vector);
1192cdfe53cSJan Kiszka } else {
1202cdfe53cSJan Kiszka msg = msix_get_message(dev, vector);
1212cdfe53cSJan Kiszka ret = dev->msix_vector_use_notifier(dev, vector, msg);
1222cdfe53cSJan Kiszka assert(ret >= 0);
1232cdfe53cSJan Kiszka }
1242cdfe53cSJan Kiszka }
1252cdfe53cSJan Kiszka
msix_handle_mask_update(PCIDevice * dev,int vector,bool was_masked)126ae392c41SMichael S. Tsirkin static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked)
127ae392c41SMichael S. Tsirkin {
128ae392c41SMichael S. Tsirkin bool is_masked = msix_is_masked(dev, vector);
1292cdfe53cSJan Kiszka
1306096cf78SDavid Woodhouse if (xen_mode == XEN_EMULATE) {
1316096cf78SDavid Woodhouse MSIMessage msg = msix_prepare_message(dev, vector);
1326096cf78SDavid Woodhouse
1336096cf78SDavid Woodhouse xen_evtchn_snoop_msi(dev, true, vector, msg.address, msg.data,
1346096cf78SDavid Woodhouse is_masked);
1356096cf78SDavid Woodhouse }
1366096cf78SDavid Woodhouse
137ae392c41SMichael S. Tsirkin if (is_masked == was_masked) {
138ae392c41SMichael S. Tsirkin return;
139ae392c41SMichael S. Tsirkin }
140ae392c41SMichael S. Tsirkin
1412cdfe53cSJan Kiszka msix_fire_vector_notifier(dev, vector, is_masked);
1422cdfe53cSJan Kiszka
143ae392c41SMichael S. Tsirkin if (!is_masked && msix_is_pending(dev, vector)) {
1445b5cb086SMichael S. Tsirkin msix_clr_pending(dev, vector);
1455b5cb086SMichael S. Tsirkin msix_notify(dev, vector);
1465b5cb086SMichael S. Tsirkin }
1475b5cb086SMichael S. Tsirkin }
1485b5cb086SMichael S. Tsirkin
msix_set_mask(PCIDevice * dev,int vector,bool mask)14915377f6eSAkihiko Odaki void msix_set_mask(PCIDevice *dev, int vector, bool mask)
15008cf3dc6SJagannathan Raman {
15108cf3dc6SJagannathan Raman unsigned offset;
15208cf3dc6SJagannathan Raman bool was_masked;
15308cf3dc6SJagannathan Raman
15415377f6eSAkihiko Odaki assert(vector < dev->msix_entries_nr);
15508cf3dc6SJagannathan Raman
15608cf3dc6SJagannathan Raman offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
15708cf3dc6SJagannathan Raman
15808cf3dc6SJagannathan Raman was_masked = msix_is_masked(dev, vector);
15908cf3dc6SJagannathan Raman
16008cf3dc6SJagannathan Raman if (mask) {
16108cf3dc6SJagannathan Raman dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
16208cf3dc6SJagannathan Raman } else {
16308cf3dc6SJagannathan Raman dev->msix_table[offset] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
16408cf3dc6SJagannathan Raman }
16508cf3dc6SJagannathan Raman
16608cf3dc6SJagannathan Raman msix_handle_mask_update(dev, vector, was_masked);
16708cf3dc6SJagannathan Raman }
16808cf3dc6SJagannathan Raman
msix_masked(PCIDevice * dev)169993b1f4bSPeter Xu static bool msix_masked(PCIDevice *dev)
170993b1f4bSPeter Xu {
171993b1f4bSPeter Xu return dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK;
172993b1f4bSPeter Xu }
173993b1f4bSPeter Xu
msix_update_function_masked(PCIDevice * dev)17450322249SMichael S. Tsirkin static void msix_update_function_masked(PCIDevice *dev)
17550322249SMichael S. Tsirkin {
176993b1f4bSPeter Xu dev->msix_function_masked = !msix_enabled(dev) || msix_masked(dev);
17750322249SMichael S. Tsirkin }
17850322249SMichael S. Tsirkin
1795b5cb086SMichael S. Tsirkin /* Handle MSI-X capability config write. */
msix_write_config(PCIDevice * dev,uint32_t addr,uint32_t val,int len)1805b5cb086SMichael S. Tsirkin void msix_write_config(PCIDevice *dev, uint32_t addr,
1815b5cb086SMichael S. Tsirkin uint32_t val, int len)
1825b5cb086SMichael S. Tsirkin {
1835b5cb086SMichael S. Tsirkin unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET;
1845b5cb086SMichael S. Tsirkin int vector;
18550322249SMichael S. Tsirkin bool was_masked;
1865b5cb086SMichael S. Tsirkin
1877c9958b0SJan Kiszka if (!msix_present(dev) || !range_covers_byte(addr, len, enable_pos)) {
1885b5cb086SMichael S. Tsirkin return;
1895b5cb086SMichael S. Tsirkin }
1905b5cb086SMichael S. Tsirkin
191993b1f4bSPeter Xu trace_msix_write_config(dev->name, msix_enabled(dev), msix_masked(dev));
192993b1f4bSPeter Xu
19350322249SMichael S. Tsirkin was_masked = dev->msix_function_masked;
19450322249SMichael S. Tsirkin msix_update_function_masked(dev);
19550322249SMichael S. Tsirkin
1965b5cb086SMichael S. Tsirkin if (!msix_enabled(dev)) {
1975b5cb086SMichael S. Tsirkin return;
1985b5cb086SMichael S. Tsirkin }
1995b5cb086SMichael S. Tsirkin
200e407bf13SIsaku Yamahata pci_device_deassert_intx(dev);
2015b5cb086SMichael S. Tsirkin
20250322249SMichael S. Tsirkin if (dev->msix_function_masked == was_masked) {
2035b5cb086SMichael S. Tsirkin return;
2045b5cb086SMichael S. Tsirkin }
2055b5cb086SMichael S. Tsirkin
2065b5cb086SMichael S. Tsirkin for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
207ae392c41SMichael S. Tsirkin msix_handle_mask_update(dev, vector,
208ae392c41SMichael S. Tsirkin msix_vector_masked(dev, vector, was_masked));
2095b5cb086SMichael S. Tsirkin }
21002eb84d0SMichael S. Tsirkin }
21102eb84d0SMichael S. Tsirkin
msix_table_mmio_read(void * opaque,hwaddr addr,unsigned size)212a8170e5eSAvi Kivity static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr,
213eebcb0a7SAlex Williamson unsigned size)
214eebcb0a7SAlex Williamson {
215eebcb0a7SAlex Williamson PCIDevice *dev = opaque;
216eebcb0a7SAlex Williamson
21758cf0f86SPaolo Bonzini assert(addr + size <= dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
218d35e428cSAlex Williamson return pci_get_long(dev->msix_table + addr);
219eebcb0a7SAlex Williamson }
220eebcb0a7SAlex Williamson
msix_table_mmio_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)221a8170e5eSAvi Kivity static void msix_table_mmio_write(void *opaque, hwaddr addr,
22295524ae8SAvi Kivity uint64_t val, unsigned size)
22302eb84d0SMichael S. Tsirkin {
22402eb84d0SMichael S. Tsirkin PCIDevice *dev = opaque;
225d35e428cSAlex Williamson int vector = addr / PCI_MSIX_ENTRY_SIZE;
226ae392c41SMichael S. Tsirkin bool was_masked;
2279a93b617SMichael S. Tsirkin
22858cf0f86SPaolo Bonzini assert(addr + size <= dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
22958cf0f86SPaolo Bonzini
230ae392c41SMichael S. Tsirkin was_masked = msix_is_masked(dev, vector);
231d35e428cSAlex Williamson pci_set_long(dev->msix_table + addr, val);
232ae392c41SMichael S. Tsirkin msix_handle_mask_update(dev, vector, was_masked);
23302eb84d0SMichael S. Tsirkin }
23402eb84d0SMichael S. Tsirkin
235d35e428cSAlex Williamson static const MemoryRegionOps msix_table_mmio_ops = {
236d35e428cSAlex Williamson .read = msix_table_mmio_read,
237d35e428cSAlex Williamson .write = msix_table_mmio_write,
23868d1e1f5SAlexander Graf .endianness = DEVICE_LITTLE_ENDIAN,
239d35e428cSAlex Williamson .valid = {
240d35e428cSAlex Williamson .min_access_size = 4,
241191f90cbSMichael S. Tsirkin .max_access_size = 8,
242191f90cbSMichael S. Tsirkin },
243191f90cbSMichael S. Tsirkin .impl = {
244d35e428cSAlex Williamson .max_access_size = 4,
245d35e428cSAlex Williamson },
246d35e428cSAlex Williamson };
247d35e428cSAlex Williamson
msix_pba_mmio_read(void * opaque,hwaddr addr,unsigned size)248a8170e5eSAvi Kivity static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr,
249d35e428cSAlex Williamson unsigned size)
250d35e428cSAlex Williamson {
251d35e428cSAlex Williamson PCIDevice *dev = opaque;
252bbef882cSMichael S. Tsirkin if (dev->msix_vector_poll_notifier) {
253bbef882cSMichael S. Tsirkin unsigned vector_start = addr * 8;
25442e2a7a0SNicholas Piggin unsigned vector_end = MIN((addr + size) * 8, dev->msix_entries_nr);
255bbef882cSMichael S. Tsirkin dev->msix_vector_poll_notifier(dev, vector_start, vector_end);
256bbef882cSMichael S. Tsirkin }
257d35e428cSAlex Williamson
258d35e428cSAlex Williamson return pci_get_long(dev->msix_pba + addr);
259d35e428cSAlex Williamson }
260d35e428cSAlex Williamson
msix_pba_mmio_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)26143b11a91SMarc-André Lureau static void msix_pba_mmio_write(void *opaque, hwaddr addr,
26243b11a91SMarc-André Lureau uint64_t val, unsigned size)
26343b11a91SMarc-André Lureau {
264*44ed44aeSNicholas Piggin PCIDevice *dev = opaque;
265*44ed44aeSNicholas Piggin
266*44ed44aeSNicholas Piggin qemu_log_mask(LOG_GUEST_ERROR,
267*44ed44aeSNicholas Piggin "PCI [%s:%02x:%02x.%x] attempt to write to MSI-X "
268*44ed44aeSNicholas Piggin "PBA at 0x%" FMT_PCIBUS ", ignoring.\n",
269*44ed44aeSNicholas Piggin pci_root_bus_path(dev), pci_dev_bus_num(dev),
270*44ed44aeSNicholas Piggin PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
271*44ed44aeSNicholas Piggin addr);
27243b11a91SMarc-André Lureau }
27343b11a91SMarc-André Lureau
274d35e428cSAlex Williamson static const MemoryRegionOps msix_pba_mmio_ops = {
275d35e428cSAlex Williamson .read = msix_pba_mmio_read,
27643b11a91SMarc-André Lureau .write = msix_pba_mmio_write,
27768d1e1f5SAlexander Graf .endianness = DEVICE_LITTLE_ENDIAN,
27895524ae8SAvi Kivity .valid = {
27995524ae8SAvi Kivity .min_access_size = 4,
280191f90cbSMichael S. Tsirkin .max_access_size = 8,
281191f90cbSMichael S. Tsirkin },
282191f90cbSMichael S. Tsirkin .impl = {
28395524ae8SAvi Kivity .max_access_size = 4,
28495524ae8SAvi Kivity },
28502eb84d0SMichael S. Tsirkin };
28602eb84d0SMichael S. Tsirkin
msix_mask_all(struct PCIDevice * dev,unsigned nentries)287ae1be0bbSMichael S. Tsirkin static void msix_mask_all(struct PCIDevice *dev, unsigned nentries)
288ae1be0bbSMichael S. Tsirkin {
289ae1be0bbSMichael S. Tsirkin int vector;
2905b5f1330SJan Kiszka
291ae1be0bbSMichael S. Tsirkin for (vector = 0; vector < nentries; ++vector) {
29201731cfbSJan Kiszka unsigned offset =
29301731cfbSJan Kiszka vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
2945b5f1330SJan Kiszka bool was_masked = msix_is_masked(dev, vector);
2955b5f1330SJan Kiszka
296d35e428cSAlex Williamson dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
2975b5f1330SJan Kiszka msix_handle_mask_update(dev, vector, was_masked);
298ae1be0bbSMichael S. Tsirkin }
299ae1be0bbSMichael S. Tsirkin }
300ae1be0bbSMichael S. Tsirkin
301ee640c62SCao jin /*
302ee640c62SCao jin * Make PCI device @dev MSI-X capable
303ee640c62SCao jin * @nentries is the max number of MSI-X vectors that the device support.
304ee640c62SCao jin * @table_bar is the MemoryRegion that MSI-X table structure resides.
305ee640c62SCao jin * @table_bar_nr is number of base address register corresponding to @table_bar.
306ee640c62SCao jin * @table_offset indicates the offset that the MSI-X table structure starts with
307ee640c62SCao jin * in @table_bar.
308ee640c62SCao jin * @pba_bar is the MemoryRegion that the Pending Bit Array structure resides.
309ee640c62SCao jin * @pba_bar_nr is number of base address register corresponding to @pba_bar.
310ee640c62SCao jin * @pba_offset indicates the offset that the Pending Bit Array structure
311ee640c62SCao jin * starts with in @pba_bar.
312ee640c62SCao jin * Non-zero @cap_pos puts capability MSI-X at that offset in PCI config space.
313ee640c62SCao jin * @errp is for returning errors.
314ee640c62SCao jin *
315ee640c62SCao jin * Return 0 on success; set @errp and return -errno on error:
316ee640c62SCao jin * -ENOTSUP means lacking msi support for a msi-capable platform.
317ee640c62SCao jin * -EINVAL means capability overlap, happens when @cap_pos is non-zero,
318ee640c62SCao jin * also means a programming error, except device assignment, which can check
319ee640c62SCao jin * if a real HW is broken.
320ee640c62SCao jin */
msix_init(struct PCIDevice * dev,unsigned short nentries,MemoryRegion * table_bar,uint8_t table_bar_nr,unsigned table_offset,MemoryRegion * pba_bar,uint8_t pba_bar_nr,unsigned pba_offset,uint8_t cap_pos,Error ** errp)32102eb84d0SMichael S. Tsirkin int msix_init(struct PCIDevice *dev, unsigned short nentries,
3225a2c2029SAlex Williamson MemoryRegion *table_bar, uint8_t table_bar_nr,
3235a2c2029SAlex Williamson unsigned table_offset, MemoryRegion *pba_bar,
324ee640c62SCao jin uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos,
325ee640c62SCao jin Error **errp)
32602eb84d0SMichael S. Tsirkin {
3275a2c2029SAlex Williamson int cap;
328d35e428cSAlex Williamson unsigned table_size, pba_size;
3295a2c2029SAlex Williamson uint8_t *config;
33002eb84d0SMichael S. Tsirkin
33160ba3cc2SJan Kiszka /* Nothing to do if MSI is not supported by interrupt controller */
332226419d6SMichael S. Tsirkin if (!msi_nonbroken) {
333ee640c62SCao jin error_setg(errp, "MSI-X is not supported by interrupt controller");
33460ba3cc2SJan Kiszka return -ENOTSUP;
33560ba3cc2SJan Kiszka }
3365a2c2029SAlex Williamson
3375a2c2029SAlex Williamson if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) {
338ee640c62SCao jin error_setg(errp, "The number of MSI-X vectors is invalid");
33902eb84d0SMichael S. Tsirkin return -EINVAL;
3405a2c2029SAlex Williamson }
34102eb84d0SMichael S. Tsirkin
342d35e428cSAlex Williamson table_size = nentries * PCI_MSIX_ENTRY_SIZE;
343d35e428cSAlex Williamson pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
344d35e428cSAlex Williamson
3455a2c2029SAlex Williamson /* Sanity test: table & pba don't overlap, fit within BARs, min aligned */
3465a2c2029SAlex Williamson if ((table_bar_nr == pba_bar_nr &&
3475a2c2029SAlex Williamson ranges_overlap(table_offset, table_size, pba_offset, pba_size)) ||
3485a2c2029SAlex Williamson table_offset + table_size > memory_region_size(table_bar) ||
3495a2c2029SAlex Williamson pba_offset + pba_size > memory_region_size(pba_bar) ||
3505a2c2029SAlex Williamson (table_offset | pba_offset) & PCI_MSIX_FLAGS_BIRMASK) {
351ee640c62SCao jin error_setg(errp, "table & pba overlap, or they don't fit in BARs,"
352ee640c62SCao jin " or don't align");
3535a2c2029SAlex Williamson return -EINVAL;
3545a2c2029SAlex Williamson }
3555a2c2029SAlex Williamson
35627841278SMao Zhongyi cap = pci_add_capability(dev, PCI_CAP_ID_MSIX,
357ee640c62SCao jin cap_pos, MSIX_CAP_LENGTH, errp);
3585a2c2029SAlex Williamson if (cap < 0) {
3595a2c2029SAlex Williamson return cap;
3605a2c2029SAlex Williamson }
3615a2c2029SAlex Williamson
3625a2c2029SAlex Williamson dev->msix_cap = cap;
3635a2c2029SAlex Williamson dev->cap_present |= QEMU_PCI_CAP_MSIX;
3645a2c2029SAlex Williamson config = dev->config + cap;
3655a2c2029SAlex Williamson
3665a2c2029SAlex Williamson pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1);
3675a2c2029SAlex Williamson dev->msix_entries_nr = nentries;
3685a2c2029SAlex Williamson dev->msix_function_masked = true;
3695a2c2029SAlex Williamson
3705a2c2029SAlex Williamson pci_set_long(config + PCI_MSIX_TABLE, table_offset | table_bar_nr);
3715a2c2029SAlex Williamson pci_set_long(config + PCI_MSIX_PBA, pba_offset | pba_bar_nr);
3725a2c2029SAlex Williamson
3735a2c2029SAlex Williamson /* Make flags bit writable. */
3745a2c2029SAlex Williamson dev->wmask[cap + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK |
3755a2c2029SAlex Williamson MSIX_MASKALL_MASK;
37602eb84d0SMichael S. Tsirkin
377d35e428cSAlex Williamson dev->msix_table = g_malloc0(table_size);
378d35e428cSAlex Williamson dev->msix_pba = g_malloc0(pba_size);
3795a2c2029SAlex Williamson dev->msix_entry_used = g_malloc0(nentries * sizeof *dev->msix_entry_used);
3805a2c2029SAlex Williamson
381ae1be0bbSMichael S. Tsirkin msix_mask_all(dev, nentries);
38202eb84d0SMichael S. Tsirkin
38340c5dce9SPaolo Bonzini memory_region_init_io(&dev->msix_table_mmio, OBJECT(dev), &msix_table_mmio_ops, dev,
384d35e428cSAlex Williamson "msix-table", table_size);
3855a2c2029SAlex Williamson memory_region_add_subregion(table_bar, table_offset, &dev->msix_table_mmio);
38640c5dce9SPaolo Bonzini memory_region_init_io(&dev->msix_pba_mmio, OBJECT(dev), &msix_pba_mmio_ops, dev,
387d35e428cSAlex Williamson "msix-pba", pba_size);
3885a2c2029SAlex Williamson memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio);
38902eb84d0SMichael S. Tsirkin
39008cf3dc6SJagannathan Raman dev->msix_prepare_message = msix_prepare_message;
39108cf3dc6SJagannathan Raman
39202eb84d0SMichael S. Tsirkin return 0;
39302eb84d0SMichael S. Tsirkin }
39402eb84d0SMichael S. Tsirkin
msix_init_exclusive_bar(PCIDevice * dev,unsigned short nentries,uint8_t bar_nr,Error ** errp)39553f94925SAlex Williamson int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
396ee640c62SCao jin uint8_t bar_nr, Error **errp)
39753f94925SAlex Williamson {
39853f94925SAlex Williamson int ret;
39953f94925SAlex Williamson char *name;
400a0ccd212SJason Wang uint32_t bar_size = 4096;
401a0ccd212SJason Wang uint32_t bar_pba_offset = bar_size / 2;
40217323e8bSDongli Zhang uint32_t bar_pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
40353f94925SAlex Williamson
40453f94925SAlex Williamson /*
40553f94925SAlex Williamson * Migration compatibility dictates that this remains a 4k
40653f94925SAlex Williamson * BAR with the vector table in the lower half and PBA in
407a0ccd212SJason Wang * the upper half for nentries which is lower or equal to 128.
408a0ccd212SJason Wang * No need to care about using more than 65 entries for legacy
409a0ccd212SJason Wang * machine types who has at most 64 queues.
41053f94925SAlex Williamson */
411a0ccd212SJason Wang if (nentries * PCI_MSIX_ENTRY_SIZE > bar_pba_offset) {
412a0ccd212SJason Wang bar_pba_offset = nentries * PCI_MSIX_ENTRY_SIZE;
413a0ccd212SJason Wang }
41453f94925SAlex Williamson
415a0ccd212SJason Wang if (bar_pba_offset + bar_pba_size > 4096) {
416a0ccd212SJason Wang bar_size = bar_pba_offset + bar_pba_size;
417a0ccd212SJason Wang }
418a0ccd212SJason Wang
4199bff5d81SPeter Maydell bar_size = pow2ceil(bar_size);
42053f94925SAlex Williamson
4215f893b4eSGerd Hoffmann name = g_strdup_printf("%s-msix", dev->name);
422a0ccd212SJason Wang memory_region_init(&dev->msix_exclusive_bar, OBJECT(dev), name, bar_size);
4235f893b4eSGerd Hoffmann g_free(name);
42453f94925SAlex Williamson
42553f94925SAlex Williamson ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr,
426a0ccd212SJason Wang 0, &dev->msix_exclusive_bar,
427a0ccd212SJason Wang bar_nr, bar_pba_offset,
428ee640c62SCao jin 0, errp);
42953f94925SAlex Williamson if (ret) {
43053f94925SAlex Williamson return ret;
43153f94925SAlex Williamson }
43253f94925SAlex Williamson
43353f94925SAlex Williamson pci_register_bar(dev, bar_nr, PCI_BASE_ADDRESS_SPACE_MEMORY,
43453f94925SAlex Williamson &dev->msix_exclusive_bar);
43553f94925SAlex Williamson
43653f94925SAlex Williamson return 0;
43753f94925SAlex Williamson }
43853f94925SAlex Williamson
msix_free_irq_entries(PCIDevice * dev)43998304c84SMichael S. Tsirkin static void msix_free_irq_entries(PCIDevice *dev)
44098304c84SMichael S. Tsirkin {
44198304c84SMichael S. Tsirkin int vector;
44298304c84SMichael S. Tsirkin
44398304c84SMichael S. Tsirkin for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
44498304c84SMichael S. Tsirkin dev->msix_entry_used[vector] = 0;
44598304c84SMichael S. Tsirkin msix_clr_pending(dev, vector);
44698304c84SMichael S. Tsirkin }
44798304c84SMichael S. Tsirkin }
44898304c84SMichael S. Tsirkin
msix_clear_all_vectors(PCIDevice * dev)4493cac001eSMichael S. Tsirkin static void msix_clear_all_vectors(PCIDevice *dev)
4503cac001eSMichael S. Tsirkin {
4513cac001eSMichael S. Tsirkin int vector;
4523cac001eSMichael S. Tsirkin
4533cac001eSMichael S. Tsirkin for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
4543cac001eSMichael S. Tsirkin msix_clr_pending(dev, vector);
4553cac001eSMichael S. Tsirkin }
4563cac001eSMichael S. Tsirkin }
4573cac001eSMichael S. Tsirkin
45802eb84d0SMichael S. Tsirkin /* Clean up resources for the device. */
msix_uninit(PCIDevice * dev,MemoryRegion * table_bar,MemoryRegion * pba_bar)459572992eeSAlex Williamson void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar)
46002eb84d0SMichael S. Tsirkin {
46144701ab7SJan Kiszka if (!msix_present(dev)) {
462572992eeSAlex Williamson return;
46344701ab7SJan Kiszka }
46402eb84d0SMichael S. Tsirkin pci_del_capability(dev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH);
46502eb84d0SMichael S. Tsirkin dev->msix_cap = 0;
46602eb84d0SMichael S. Tsirkin msix_free_irq_entries(dev);
46702eb84d0SMichael S. Tsirkin dev->msix_entries_nr = 0;
4685a2c2029SAlex Williamson memory_region_del_subregion(pba_bar, &dev->msix_pba_mmio);
469d35e428cSAlex Williamson g_free(dev->msix_pba);
470d35e428cSAlex Williamson dev->msix_pba = NULL;
4715a2c2029SAlex Williamson memory_region_del_subregion(table_bar, &dev->msix_table_mmio);
472d35e428cSAlex Williamson g_free(dev->msix_table);
473d35e428cSAlex Williamson dev->msix_table = NULL;
4747267c094SAnthony Liguori g_free(dev->msix_entry_used);
47502eb84d0SMichael S. Tsirkin dev->msix_entry_used = NULL;
47602eb84d0SMichael S. Tsirkin dev->cap_present &= ~QEMU_PCI_CAP_MSIX;
47708cf3dc6SJagannathan Raman dev->msix_prepare_message = NULL;
47802eb84d0SMichael S. Tsirkin }
47902eb84d0SMichael S. Tsirkin
msix_uninit_exclusive_bar(PCIDevice * dev)48053f94925SAlex Williamson void msix_uninit_exclusive_bar(PCIDevice *dev)
48153f94925SAlex Williamson {
48253f94925SAlex Williamson if (msix_present(dev)) {
4835a2c2029SAlex Williamson msix_uninit(dev, &dev->msix_exclusive_bar, &dev->msix_exclusive_bar);
48453f94925SAlex Williamson }
48553f94925SAlex Williamson }
48653f94925SAlex Williamson
msix_save(PCIDevice * dev,QEMUFile * f)48702eb84d0SMichael S. Tsirkin void msix_save(PCIDevice *dev, QEMUFile *f)
48802eb84d0SMichael S. Tsirkin {
4899a3e12c8SMichael S. Tsirkin unsigned n = dev->msix_entries_nr;
4909a3e12c8SMichael S. Tsirkin
49144701ab7SJan Kiszka if (!msix_present(dev)) {
4929a3e12c8SMichael S. Tsirkin return;
49372755a70SMichael S. Tsirkin }
4949a3e12c8SMichael S. Tsirkin
495d35e428cSAlex Williamson qemu_put_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
4960ef1efcfSMarc-André Lureau qemu_put_buffer(f, dev->msix_pba, DIV_ROUND_UP(n, 8));
49702eb84d0SMichael S. Tsirkin }
49802eb84d0SMichael S. Tsirkin
49902eb84d0SMichael S. Tsirkin /* Should be called after restoring the config space. */
msix_load(PCIDevice * dev,QEMUFile * f)50002eb84d0SMichael S. Tsirkin void msix_load(PCIDevice *dev, QEMUFile *f)
50102eb84d0SMichael S. Tsirkin {
50202eb84d0SMichael S. Tsirkin unsigned n = dev->msix_entries_nr;
5032cdfe53cSJan Kiszka unsigned int vector;
50402eb84d0SMichael S. Tsirkin
50544701ab7SJan Kiszka if (!msix_present(dev)) {
50602eb84d0SMichael S. Tsirkin return;
50798846d73SBlue Swirl }
50802eb84d0SMichael S. Tsirkin
5093cac001eSMichael S. Tsirkin msix_clear_all_vectors(dev);
510d35e428cSAlex Williamson qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
5110ef1efcfSMarc-André Lureau qemu_get_buffer(f, dev->msix_pba, DIV_ROUND_UP(n, 8));
51250322249SMichael S. Tsirkin msix_update_function_masked(dev);
5132cdfe53cSJan Kiszka
5142cdfe53cSJan Kiszka for (vector = 0; vector < n; vector++) {
5152cdfe53cSJan Kiszka msix_handle_mask_update(dev, vector, true);
5162cdfe53cSJan Kiszka }
51702eb84d0SMichael S. Tsirkin }
51802eb84d0SMichael S. Tsirkin
51902eb84d0SMichael S. Tsirkin /* Does device support MSI-X? */
msix_present(PCIDevice * dev)52002eb84d0SMichael S. Tsirkin int msix_present(PCIDevice *dev)
52102eb84d0SMichael S. Tsirkin {
52202eb84d0SMichael S. Tsirkin return dev->cap_present & QEMU_PCI_CAP_MSIX;
52302eb84d0SMichael S. Tsirkin }
52402eb84d0SMichael S. Tsirkin
52502eb84d0SMichael S. Tsirkin /* Is MSI-X enabled? */
msix_enabled(PCIDevice * dev)52602eb84d0SMichael S. Tsirkin int msix_enabled(PCIDevice *dev)
52702eb84d0SMichael S. Tsirkin {
52802eb84d0SMichael S. Tsirkin return (dev->cap_present & QEMU_PCI_CAP_MSIX) &&
5292760952bSMichael S. Tsirkin (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
53002eb84d0SMichael S. Tsirkin MSIX_ENABLE_MASK);
53102eb84d0SMichael S. Tsirkin }
53202eb84d0SMichael S. Tsirkin
53302eb84d0SMichael S. Tsirkin /* Send an MSI-X message */
msix_notify(PCIDevice * dev,unsigned vector)53402eb84d0SMichael S. Tsirkin void msix_notify(PCIDevice *dev, unsigned vector)
53502eb84d0SMichael S. Tsirkin {
536bc4caf49SJan Kiszka MSIMessage msg;
53702eb84d0SMichael S. Tsirkin
53815377f6eSAkihiko Odaki assert(vector < dev->msix_entries_nr);
53915377f6eSAkihiko Odaki
54015377f6eSAkihiko Odaki if (!dev->msix_entry_used[vector]) {
54102eb84d0SMichael S. Tsirkin return;
54293482436SCao jin }
54393482436SCao jin
54402eb84d0SMichael S. Tsirkin if (msix_is_masked(dev, vector)) {
54502eb84d0SMichael S. Tsirkin msix_set_pending(dev, vector);
54602eb84d0SMichael S. Tsirkin return;
54702eb84d0SMichael S. Tsirkin }
54802eb84d0SMichael S. Tsirkin
549bc4caf49SJan Kiszka msg = msix_get_message(dev, vector);
550bc4caf49SJan Kiszka
55138d40ff1SPavel Fedin msi_send_message(dev, msg);
55202eb84d0SMichael S. Tsirkin }
55302eb84d0SMichael S. Tsirkin
msix_reset(PCIDevice * dev)55402eb84d0SMichael S. Tsirkin void msix_reset(PCIDevice *dev)
55502eb84d0SMichael S. Tsirkin {
55644701ab7SJan Kiszka if (!msix_present(dev)) {
55702eb84d0SMichael S. Tsirkin return;
55844701ab7SJan Kiszka }
5593cac001eSMichael S. Tsirkin msix_clear_all_vectors(dev);
5602760952bSMichael S. Tsirkin dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &=
5612760952bSMichael S. Tsirkin ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET];
562d35e428cSAlex Williamson memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
563d35e428cSAlex Williamson memset(dev->msix_pba, 0, QEMU_ALIGN_UP(dev->msix_entries_nr, 64) / 8);
564ae1be0bbSMichael S. Tsirkin msix_mask_all(dev, dev->msix_entries_nr);
56502eb84d0SMichael S. Tsirkin }
56602eb84d0SMichael S. Tsirkin
56702eb84d0SMichael S. Tsirkin /* PCI spec suggests that devices make it possible for software to configure
56802eb84d0SMichael S. Tsirkin * less vectors than supported by the device, but does not specify a standard
56902eb84d0SMichael S. Tsirkin * mechanism for devices to do so.
57002eb84d0SMichael S. Tsirkin *
57102eb84d0SMichael S. Tsirkin * We support this by asking devices to declare vectors software is going to
57202eb84d0SMichael S. Tsirkin * actually use, and checking this on the notification path. Devices that
57302eb84d0SMichael S. Tsirkin * don't want to follow the spec suggestion can declare all vectors as used. */
57402eb84d0SMichael S. Tsirkin
57502eb84d0SMichael S. Tsirkin /* Mark vector as used. */
msix_vector_use(PCIDevice * dev,unsigned vector)57615377f6eSAkihiko Odaki void msix_vector_use(PCIDevice *dev, unsigned vector)
57702eb84d0SMichael S. Tsirkin {
57815377f6eSAkihiko Odaki assert(vector < dev->msix_entries_nr);
57902eb84d0SMichael S. Tsirkin dev->msix_entry_used[vector]++;
58002eb84d0SMichael S. Tsirkin }
58102eb84d0SMichael S. Tsirkin
58202eb84d0SMichael S. Tsirkin /* Mark vector as unused. */
msix_vector_unuse(PCIDevice * dev,unsigned vector)58302eb84d0SMichael S. Tsirkin void msix_vector_unuse(PCIDevice *dev, unsigned vector)
58402eb84d0SMichael S. Tsirkin {
58515377f6eSAkihiko Odaki assert(vector < dev->msix_entries_nr);
58615377f6eSAkihiko Odaki if (!dev->msix_entry_used[vector]) {
58798304c84SMichael S. Tsirkin return;
58898304c84SMichael S. Tsirkin }
58998304c84SMichael S. Tsirkin if (--dev->msix_entry_used[vector]) {
59098304c84SMichael S. Tsirkin return;
59198304c84SMichael S. Tsirkin }
59298304c84SMichael S. Tsirkin msix_clr_pending(dev, vector);
59302eb84d0SMichael S. Tsirkin }
594b5f28bcaSMichael S. Tsirkin
msix_unuse_all_vectors(PCIDevice * dev)595b5f28bcaSMichael S. Tsirkin void msix_unuse_all_vectors(PCIDevice *dev)
596b5f28bcaSMichael S. Tsirkin {
59744701ab7SJan Kiszka if (!msix_present(dev)) {
598b5f28bcaSMichael S. Tsirkin return;
59944701ab7SJan Kiszka }
600b5f28bcaSMichael S. Tsirkin msix_free_irq_entries(dev);
601b5f28bcaSMichael S. Tsirkin }
6022cdfe53cSJan Kiszka
msix_nr_vectors_allocated(const PCIDevice * dev)603cb697aaaSJan Kiszka unsigned int msix_nr_vectors_allocated(const PCIDevice *dev)
604cb697aaaSJan Kiszka {
605cb697aaaSJan Kiszka return dev->msix_entries_nr;
606cb697aaaSJan Kiszka }
607cb697aaaSJan Kiszka
msix_set_notifier_for_vector(PCIDevice * dev,unsigned int vector)6082cdfe53cSJan Kiszka static int msix_set_notifier_for_vector(PCIDevice *dev, unsigned int vector)
6092cdfe53cSJan Kiszka {
6102cdfe53cSJan Kiszka MSIMessage msg;
6112cdfe53cSJan Kiszka
6122cdfe53cSJan Kiszka if (msix_is_masked(dev, vector)) {
6132cdfe53cSJan Kiszka return 0;
6142cdfe53cSJan Kiszka }
6152cdfe53cSJan Kiszka msg = msix_get_message(dev, vector);
6162cdfe53cSJan Kiszka return dev->msix_vector_use_notifier(dev, vector, msg);
6172cdfe53cSJan Kiszka }
6182cdfe53cSJan Kiszka
msix_unset_notifier_for_vector(PCIDevice * dev,unsigned int vector)6192cdfe53cSJan Kiszka static void msix_unset_notifier_for_vector(PCIDevice *dev, unsigned int vector)
6202cdfe53cSJan Kiszka {
6212cdfe53cSJan Kiszka if (msix_is_masked(dev, vector)) {
6222cdfe53cSJan Kiszka return;
6232cdfe53cSJan Kiszka }
6242cdfe53cSJan Kiszka dev->msix_vector_release_notifier(dev, vector);
6252cdfe53cSJan Kiszka }
6262cdfe53cSJan Kiszka
msix_set_vector_notifiers(PCIDevice * dev,MSIVectorUseNotifier use_notifier,MSIVectorReleaseNotifier release_notifier,MSIVectorPollNotifier poll_notifier)6272cdfe53cSJan Kiszka int msix_set_vector_notifiers(PCIDevice *dev,
6282cdfe53cSJan Kiszka MSIVectorUseNotifier use_notifier,
629bbef882cSMichael S. Tsirkin MSIVectorReleaseNotifier release_notifier,
630bbef882cSMichael S. Tsirkin MSIVectorPollNotifier poll_notifier)
6312cdfe53cSJan Kiszka {
6322cdfe53cSJan Kiszka int vector, ret;
6332cdfe53cSJan Kiszka
6342cdfe53cSJan Kiszka assert(use_notifier && release_notifier);
6352cdfe53cSJan Kiszka
6362cdfe53cSJan Kiszka dev->msix_vector_use_notifier = use_notifier;
6372cdfe53cSJan Kiszka dev->msix_vector_release_notifier = release_notifier;
638bbef882cSMichael S. Tsirkin dev->msix_vector_poll_notifier = poll_notifier;
6392cdfe53cSJan Kiszka
6402cdfe53cSJan Kiszka if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
6412cdfe53cSJan Kiszka (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) {
6422cdfe53cSJan Kiszka for (vector = 0; vector < dev->msix_entries_nr; vector++) {
6432cdfe53cSJan Kiszka ret = msix_set_notifier_for_vector(dev, vector);
6442cdfe53cSJan Kiszka if (ret < 0) {
6452cdfe53cSJan Kiszka goto undo;
6462cdfe53cSJan Kiszka }
6472cdfe53cSJan Kiszka }
6482cdfe53cSJan Kiszka }
649bbef882cSMichael S. Tsirkin if (dev->msix_vector_poll_notifier) {
650bbef882cSMichael S. Tsirkin dev->msix_vector_poll_notifier(dev, 0, dev->msix_entries_nr);
651bbef882cSMichael S. Tsirkin }
6522cdfe53cSJan Kiszka return 0;
6532cdfe53cSJan Kiszka
6542cdfe53cSJan Kiszka undo:
6552cdfe53cSJan Kiszka while (--vector >= 0) {
6562cdfe53cSJan Kiszka msix_unset_notifier_for_vector(dev, vector);
6572cdfe53cSJan Kiszka }
6582cdfe53cSJan Kiszka dev->msix_vector_use_notifier = NULL;
6592cdfe53cSJan Kiszka dev->msix_vector_release_notifier = NULL;
6602d37fe9eSRobert Hoo dev->msix_vector_poll_notifier = NULL;
6612cdfe53cSJan Kiszka return ret;
6622cdfe53cSJan Kiszka }
6632cdfe53cSJan Kiszka
msix_unset_vector_notifiers(PCIDevice * dev)6642cdfe53cSJan Kiszka void msix_unset_vector_notifiers(PCIDevice *dev)
6652cdfe53cSJan Kiszka {
6662cdfe53cSJan Kiszka int vector;
6672cdfe53cSJan Kiszka
6682cdfe53cSJan Kiszka assert(dev->msix_vector_use_notifier &&
6692cdfe53cSJan Kiszka dev->msix_vector_release_notifier);
6702cdfe53cSJan Kiszka
6712cdfe53cSJan Kiszka if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
6722cdfe53cSJan Kiszka (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) {
6732cdfe53cSJan Kiszka for (vector = 0; vector < dev->msix_entries_nr; vector++) {
6742cdfe53cSJan Kiszka msix_unset_notifier_for_vector(dev, vector);
6752cdfe53cSJan Kiszka }
6762cdfe53cSJan Kiszka }
6772cdfe53cSJan Kiszka dev->msix_vector_use_notifier = NULL;
6782cdfe53cSJan Kiszka dev->msix_vector_release_notifier = NULL;
679bbef882cSMichael S. Tsirkin dev->msix_vector_poll_notifier = NULL;
6802cdfe53cSJan Kiszka }
681340b50c7SGerd Hoffmann
put_msix_state(QEMUFile * f,void * pv,size_t size,const VMStateField * field,JSONWriter * vmdesc)6822c21ee76SJianjun Duan static int put_msix_state(QEMUFile *f, void *pv, size_t size,
6833ddba9a9SMarkus Armbruster const VMStateField *field, JSONWriter *vmdesc)
684340b50c7SGerd Hoffmann {
685340b50c7SGerd Hoffmann msix_save(pv, f);
6862c21ee76SJianjun Duan
6872c21ee76SJianjun Duan return 0;
688340b50c7SGerd Hoffmann }
689340b50c7SGerd Hoffmann
get_msix_state(QEMUFile * f,void * pv,size_t size,const VMStateField * field)6902c21ee76SJianjun Duan static int get_msix_state(QEMUFile *f, void *pv, size_t size,
69103fee66fSMarc-André Lureau const VMStateField *field)
692340b50c7SGerd Hoffmann {
693340b50c7SGerd Hoffmann msix_load(pv, f);
694340b50c7SGerd Hoffmann return 0;
695340b50c7SGerd Hoffmann }
696340b50c7SGerd Hoffmann
6978e5e0890SRichard Henderson static const VMStateInfo vmstate_info_msix = {
698340b50c7SGerd Hoffmann .name = "msix state",
699340b50c7SGerd Hoffmann .get = get_msix_state,
700340b50c7SGerd Hoffmann .put = put_msix_state,
701340b50c7SGerd Hoffmann };
702340b50c7SGerd Hoffmann
703340b50c7SGerd Hoffmann const VMStateDescription vmstate_msix = {
704340b50c7SGerd Hoffmann .name = "msix",
7058e5e0890SRichard Henderson .fields = (const VMStateField[]) {
706340b50c7SGerd Hoffmann {
707340b50c7SGerd Hoffmann .name = "msix",
708340b50c7SGerd Hoffmann .version_id = 0,
709340b50c7SGerd Hoffmann .field_exists = NULL,
710340b50c7SGerd Hoffmann .size = 0, /* ouch */
711340b50c7SGerd Hoffmann .info = &vmstate_info_msix,
712340b50c7SGerd Hoffmann .flags = VMS_SINGLE,
713340b50c7SGerd Hoffmann .offset = 0,
714340b50c7SGerd Hoffmann },
715340b50c7SGerd Hoffmann VMSTATE_END_OF_LIST()
716340b50c7SGerd Hoffmann }
717340b50c7SGerd Hoffmann };
718