xref: /qemu/hw/pci/msix.c (revision 340b50c759d6b4ef33e514c40afcc799c0d7df7a)
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 
17c759b24fSMichael S. Tsirkin #include "hw/hw.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"
211de7afc9SPaolo Bonzini #include "qemu/range.h"
2202eb84d0SMichael S. Tsirkin 
2302eb84d0SMichael S. Tsirkin #define MSIX_CAP_LENGTH 12
2402eb84d0SMichael S. Tsirkin 
252760952bSMichael S. Tsirkin /* MSI enable bit and maskall bit are in byte 1 in FLAGS register */
262760952bSMichael S. Tsirkin #define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1)
2702eb84d0SMichael S. Tsirkin #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8)
285b5cb086SMichael S. Tsirkin #define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8)
2902eb84d0SMichael S. Tsirkin 
304c93bfa9SMichael S. Tsirkin MSIMessage msix_get_message(PCIDevice *dev, unsigned vector)
31bc4caf49SJan Kiszka {
32d35e428cSAlex Williamson     uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE;
33bc4caf49SJan Kiszka     MSIMessage msg;
34bc4caf49SJan Kiszka 
35bc4caf49SJan Kiszka     msg.address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR);
36bc4caf49SJan Kiszka     msg.data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA);
37bc4caf49SJan Kiszka     return msg;
38bc4caf49SJan Kiszka }
3902eb84d0SMichael S. Tsirkin 
40932d4a42SAlexey Kardashevskiy /*
41932d4a42SAlexey Kardashevskiy  * Special API for POWER to configure the vectors through
42932d4a42SAlexey Kardashevskiy  * a side channel. Should never be used by devices.
43932d4a42SAlexey Kardashevskiy  */
44932d4a42SAlexey Kardashevskiy void msix_set_message(PCIDevice *dev, int vector, struct MSIMessage msg)
45932d4a42SAlexey Kardashevskiy {
46932d4a42SAlexey Kardashevskiy     uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE;
47932d4a42SAlexey Kardashevskiy 
48932d4a42SAlexey Kardashevskiy     pci_set_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR, msg.address);
49932d4a42SAlexey Kardashevskiy     pci_set_long(table_entry + PCI_MSIX_ENTRY_DATA, msg.data);
50932d4a42SAlexey Kardashevskiy     table_entry[PCI_MSIX_ENTRY_VECTOR_CTRL] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
51932d4a42SAlexey Kardashevskiy }
52932d4a42SAlexey Kardashevskiy 
5302eb84d0SMichael S. Tsirkin static uint8_t msix_pending_mask(int vector)
5402eb84d0SMichael S. Tsirkin {
5502eb84d0SMichael S. Tsirkin     return 1 << (vector % 8);
5602eb84d0SMichael S. Tsirkin }
5702eb84d0SMichael S. Tsirkin 
5802eb84d0SMichael S. Tsirkin static uint8_t *msix_pending_byte(PCIDevice *dev, int vector)
5902eb84d0SMichael S. Tsirkin {
60d35e428cSAlex Williamson     return dev->msix_pba + vector / 8;
6102eb84d0SMichael S. Tsirkin }
6202eb84d0SMichael S. Tsirkin 
6302eb84d0SMichael S. Tsirkin static int msix_is_pending(PCIDevice *dev, int vector)
6402eb84d0SMichael S. Tsirkin {
6502eb84d0SMichael S. Tsirkin     return *msix_pending_byte(dev, vector) & msix_pending_mask(vector);
6602eb84d0SMichael S. Tsirkin }
6702eb84d0SMichael S. Tsirkin 
6870f8ee39SMichael S. Tsirkin void msix_set_pending(PCIDevice *dev, unsigned int vector)
6902eb84d0SMichael S. Tsirkin {
7002eb84d0SMichael S. Tsirkin     *msix_pending_byte(dev, vector) |= msix_pending_mask(vector);
7102eb84d0SMichael S. Tsirkin }
7202eb84d0SMichael S. Tsirkin 
7302eb84d0SMichael S. Tsirkin static void msix_clr_pending(PCIDevice *dev, int vector)
7402eb84d0SMichael S. Tsirkin {
7502eb84d0SMichael S. Tsirkin     *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector);
7602eb84d0SMichael S. Tsirkin }
7702eb84d0SMichael S. Tsirkin 
7870f8ee39SMichael S. Tsirkin static bool msix_vector_masked(PCIDevice *dev, unsigned int vector, bool fmask)
7902eb84d0SMichael S. Tsirkin {
80ae392c41SMichael S. Tsirkin     unsigned offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
81d35e428cSAlex Williamson     return fmask || dev->msix_table[offset] & PCI_MSIX_ENTRY_CTRL_MASKBIT;
825b5cb086SMichael S. Tsirkin }
835b5cb086SMichael S. Tsirkin 
8470f8ee39SMichael S. Tsirkin bool msix_is_masked(PCIDevice *dev, unsigned int vector)
855b5cb086SMichael S. Tsirkin {
86ae392c41SMichael S. Tsirkin     return msix_vector_masked(dev, vector, dev->msix_function_masked);
87ae392c41SMichael S. Tsirkin }
88ae392c41SMichael S. Tsirkin 
892cdfe53cSJan Kiszka static void msix_fire_vector_notifier(PCIDevice *dev,
902cdfe53cSJan Kiszka                                       unsigned int vector, bool is_masked)
912cdfe53cSJan Kiszka {
922cdfe53cSJan Kiszka     MSIMessage msg;
932cdfe53cSJan Kiszka     int ret;
942cdfe53cSJan Kiszka 
952cdfe53cSJan Kiszka     if (!dev->msix_vector_use_notifier) {
962cdfe53cSJan Kiszka         return;
972cdfe53cSJan Kiszka     }
982cdfe53cSJan Kiszka     if (is_masked) {
992cdfe53cSJan Kiszka         dev->msix_vector_release_notifier(dev, vector);
1002cdfe53cSJan Kiszka     } else {
1012cdfe53cSJan Kiszka         msg = msix_get_message(dev, vector);
1022cdfe53cSJan Kiszka         ret = dev->msix_vector_use_notifier(dev, vector, msg);
1032cdfe53cSJan Kiszka         assert(ret >= 0);
1042cdfe53cSJan Kiszka     }
1052cdfe53cSJan Kiszka }
1062cdfe53cSJan Kiszka 
107ae392c41SMichael S. Tsirkin static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked)
108ae392c41SMichael S. Tsirkin {
109ae392c41SMichael S. Tsirkin     bool is_masked = msix_is_masked(dev, vector);
1102cdfe53cSJan Kiszka 
111ae392c41SMichael S. Tsirkin     if (is_masked == was_masked) {
112ae392c41SMichael S. Tsirkin         return;
113ae392c41SMichael S. Tsirkin     }
114ae392c41SMichael S. Tsirkin 
1152cdfe53cSJan Kiszka     msix_fire_vector_notifier(dev, vector, is_masked);
1162cdfe53cSJan Kiszka 
117ae392c41SMichael S. Tsirkin     if (!is_masked && msix_is_pending(dev, vector)) {
1185b5cb086SMichael S. Tsirkin         msix_clr_pending(dev, vector);
1195b5cb086SMichael S. Tsirkin         msix_notify(dev, vector);
1205b5cb086SMichael S. Tsirkin     }
1215b5cb086SMichael S. Tsirkin }
1225b5cb086SMichael S. Tsirkin 
12350322249SMichael S. Tsirkin static void msix_update_function_masked(PCIDevice *dev)
12450322249SMichael S. Tsirkin {
12550322249SMichael S. Tsirkin     dev->msix_function_masked = !msix_enabled(dev) ||
12650322249SMichael S. Tsirkin         (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK);
12750322249SMichael S. Tsirkin }
12850322249SMichael S. Tsirkin 
1295b5cb086SMichael S. Tsirkin /* Handle MSI-X capability config write. */
1305b5cb086SMichael S. Tsirkin void msix_write_config(PCIDevice *dev, uint32_t addr,
1315b5cb086SMichael S. Tsirkin                        uint32_t val, int len)
1325b5cb086SMichael S. Tsirkin {
1335b5cb086SMichael S. Tsirkin     unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET;
1345b5cb086SMichael S. Tsirkin     int vector;
13550322249SMichael S. Tsirkin     bool was_masked;
1365b5cb086SMichael S. Tsirkin 
1377c9958b0SJan Kiszka     if (!msix_present(dev) || !range_covers_byte(addr, len, enable_pos)) {
1385b5cb086SMichael S. Tsirkin         return;
1395b5cb086SMichael S. Tsirkin     }
1405b5cb086SMichael S. Tsirkin 
14150322249SMichael S. Tsirkin     was_masked = dev->msix_function_masked;
14250322249SMichael S. Tsirkin     msix_update_function_masked(dev);
14350322249SMichael S. Tsirkin 
1445b5cb086SMichael S. Tsirkin     if (!msix_enabled(dev)) {
1455b5cb086SMichael S. Tsirkin         return;
1465b5cb086SMichael S. Tsirkin     }
1475b5cb086SMichael S. Tsirkin 
148e407bf13SIsaku Yamahata     pci_device_deassert_intx(dev);
1495b5cb086SMichael S. Tsirkin 
15050322249SMichael S. Tsirkin     if (dev->msix_function_masked == was_masked) {
1515b5cb086SMichael S. Tsirkin         return;
1525b5cb086SMichael S. Tsirkin     }
1535b5cb086SMichael S. Tsirkin 
1545b5cb086SMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
155ae392c41SMichael S. Tsirkin         msix_handle_mask_update(dev, vector,
156ae392c41SMichael S. Tsirkin                                 msix_vector_masked(dev, vector, was_masked));
1575b5cb086SMichael S. Tsirkin     }
15802eb84d0SMichael S. Tsirkin }
15902eb84d0SMichael S. Tsirkin 
160a8170e5eSAvi Kivity static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr,
161eebcb0a7SAlex Williamson                                      unsigned size)
162eebcb0a7SAlex Williamson {
163eebcb0a7SAlex Williamson     PCIDevice *dev = opaque;
164eebcb0a7SAlex Williamson 
165d35e428cSAlex Williamson     return pci_get_long(dev->msix_table + addr);
166eebcb0a7SAlex Williamson }
167eebcb0a7SAlex Williamson 
168a8170e5eSAvi Kivity static void msix_table_mmio_write(void *opaque, hwaddr addr,
16995524ae8SAvi Kivity                                   uint64_t val, unsigned size)
17002eb84d0SMichael S. Tsirkin {
17102eb84d0SMichael S. Tsirkin     PCIDevice *dev = opaque;
172d35e428cSAlex Williamson     int vector = addr / PCI_MSIX_ENTRY_SIZE;
173ae392c41SMichael S. Tsirkin     bool was_masked;
1749a93b617SMichael S. Tsirkin 
175ae392c41SMichael S. Tsirkin     was_masked = msix_is_masked(dev, vector);
176d35e428cSAlex Williamson     pci_set_long(dev->msix_table + addr, val);
177ae392c41SMichael S. Tsirkin     msix_handle_mask_update(dev, vector, was_masked);
17802eb84d0SMichael S. Tsirkin }
17902eb84d0SMichael S. Tsirkin 
180d35e428cSAlex Williamson static const MemoryRegionOps msix_table_mmio_ops = {
181d35e428cSAlex Williamson     .read = msix_table_mmio_read,
182d35e428cSAlex Williamson     .write = msix_table_mmio_write,
18368d1e1f5SAlexander Graf     .endianness = DEVICE_LITTLE_ENDIAN,
184d35e428cSAlex Williamson     .valid = {
185d35e428cSAlex Williamson         .min_access_size = 4,
186d35e428cSAlex Williamson         .max_access_size = 4,
187d35e428cSAlex Williamson     },
188d35e428cSAlex Williamson };
189d35e428cSAlex Williamson 
190a8170e5eSAvi Kivity static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr,
191d35e428cSAlex Williamson                                    unsigned size)
192d35e428cSAlex Williamson {
193d35e428cSAlex Williamson     PCIDevice *dev = opaque;
194bbef882cSMichael S. Tsirkin     if (dev->msix_vector_poll_notifier) {
195bbef882cSMichael S. Tsirkin         unsigned vector_start = addr * 8;
196bbef882cSMichael S. Tsirkin         unsigned vector_end = MIN(addr + size * 8, dev->msix_entries_nr);
197bbef882cSMichael S. Tsirkin         dev->msix_vector_poll_notifier(dev, vector_start, vector_end);
198bbef882cSMichael S. Tsirkin     }
199d35e428cSAlex Williamson 
200d35e428cSAlex Williamson     return pci_get_long(dev->msix_pba + addr);
201d35e428cSAlex Williamson }
202d35e428cSAlex Williamson 
203d35e428cSAlex Williamson static const MemoryRegionOps msix_pba_mmio_ops = {
204d35e428cSAlex Williamson     .read = msix_pba_mmio_read,
20568d1e1f5SAlexander Graf     .endianness = DEVICE_LITTLE_ENDIAN,
20695524ae8SAvi Kivity     .valid = {
20795524ae8SAvi Kivity         .min_access_size = 4,
20895524ae8SAvi Kivity         .max_access_size = 4,
20995524ae8SAvi Kivity     },
21002eb84d0SMichael S. Tsirkin };
21102eb84d0SMichael S. Tsirkin 
212ae1be0bbSMichael S. Tsirkin static void msix_mask_all(struct PCIDevice *dev, unsigned nentries)
213ae1be0bbSMichael S. Tsirkin {
214ae1be0bbSMichael S. Tsirkin     int vector;
2155b5f1330SJan Kiszka 
216ae1be0bbSMichael S. Tsirkin     for (vector = 0; vector < nentries; ++vector) {
21701731cfbSJan Kiszka         unsigned offset =
21801731cfbSJan Kiszka             vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
2195b5f1330SJan Kiszka         bool was_masked = msix_is_masked(dev, vector);
2205b5f1330SJan Kiszka 
221d35e428cSAlex Williamson         dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
2225b5f1330SJan Kiszka         msix_handle_mask_update(dev, vector, was_masked);
223ae1be0bbSMichael S. Tsirkin     }
224ae1be0bbSMichael S. Tsirkin }
225ae1be0bbSMichael S. Tsirkin 
2265a2c2029SAlex Williamson /* Initialize the MSI-X structures */
22702eb84d0SMichael S. Tsirkin int msix_init(struct PCIDevice *dev, unsigned short nentries,
2285a2c2029SAlex Williamson               MemoryRegion *table_bar, uint8_t table_bar_nr,
2295a2c2029SAlex Williamson               unsigned table_offset, MemoryRegion *pba_bar,
2305a2c2029SAlex Williamson               uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos)
23102eb84d0SMichael S. Tsirkin {
2325a2c2029SAlex Williamson     int cap;
233d35e428cSAlex Williamson     unsigned table_size, pba_size;
2345a2c2029SAlex Williamson     uint8_t *config;
23502eb84d0SMichael S. Tsirkin 
23660ba3cc2SJan Kiszka     /* Nothing to do if MSI is not supported by interrupt controller */
23760ba3cc2SJan Kiszka     if (!msi_supported) {
23860ba3cc2SJan Kiszka         return -ENOTSUP;
23960ba3cc2SJan Kiszka     }
2405a2c2029SAlex Williamson 
2415a2c2029SAlex Williamson     if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) {
24202eb84d0SMichael S. Tsirkin         return -EINVAL;
2435a2c2029SAlex Williamson     }
24402eb84d0SMichael S. Tsirkin 
245d35e428cSAlex Williamson     table_size = nentries * PCI_MSIX_ENTRY_SIZE;
246d35e428cSAlex Williamson     pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
247d35e428cSAlex Williamson 
2485a2c2029SAlex Williamson     /* Sanity test: table & pba don't overlap, fit within BARs, min aligned */
2495a2c2029SAlex Williamson     if ((table_bar_nr == pba_bar_nr &&
2505a2c2029SAlex Williamson          ranges_overlap(table_offset, table_size, pba_offset, pba_size)) ||
2515a2c2029SAlex Williamson         table_offset + table_size > memory_region_size(table_bar) ||
2525a2c2029SAlex Williamson         pba_offset + pba_size > memory_region_size(pba_bar) ||
2535a2c2029SAlex Williamson         (table_offset | pba_offset) & PCI_MSIX_FLAGS_BIRMASK) {
2545a2c2029SAlex Williamson         return -EINVAL;
2555a2c2029SAlex Williamson     }
2565a2c2029SAlex Williamson 
2575a2c2029SAlex Williamson     cap = pci_add_capability(dev, PCI_CAP_ID_MSIX, cap_pos, MSIX_CAP_LENGTH);
2585a2c2029SAlex Williamson     if (cap < 0) {
2595a2c2029SAlex Williamson         return cap;
2605a2c2029SAlex Williamson     }
2615a2c2029SAlex Williamson 
2625a2c2029SAlex Williamson     dev->msix_cap = cap;
2635a2c2029SAlex Williamson     dev->cap_present |= QEMU_PCI_CAP_MSIX;
2645a2c2029SAlex Williamson     config = dev->config + cap;
2655a2c2029SAlex Williamson 
2665a2c2029SAlex Williamson     pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1);
2675a2c2029SAlex Williamson     dev->msix_entries_nr = nentries;
2685a2c2029SAlex Williamson     dev->msix_function_masked = true;
2695a2c2029SAlex Williamson 
2705a2c2029SAlex Williamson     pci_set_long(config + PCI_MSIX_TABLE, table_offset | table_bar_nr);
2715a2c2029SAlex Williamson     pci_set_long(config + PCI_MSIX_PBA, pba_offset | pba_bar_nr);
2725a2c2029SAlex Williamson 
2735a2c2029SAlex Williamson     /* Make flags bit writable. */
2745a2c2029SAlex Williamson     dev->wmask[cap + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK |
2755a2c2029SAlex Williamson                                              MSIX_MASKALL_MASK;
27602eb84d0SMichael S. Tsirkin 
277d35e428cSAlex Williamson     dev->msix_table = g_malloc0(table_size);
278d35e428cSAlex Williamson     dev->msix_pba = g_malloc0(pba_size);
2795a2c2029SAlex Williamson     dev->msix_entry_used = g_malloc0(nentries * sizeof *dev->msix_entry_used);
2805a2c2029SAlex Williamson 
281ae1be0bbSMichael S. Tsirkin     msix_mask_all(dev, nentries);
28202eb84d0SMichael S. Tsirkin 
283d35e428cSAlex Williamson     memory_region_init_io(&dev->msix_table_mmio, &msix_table_mmio_ops, dev,
284d35e428cSAlex Williamson                           "msix-table", table_size);
2855a2c2029SAlex Williamson     memory_region_add_subregion(table_bar, table_offset, &dev->msix_table_mmio);
286d35e428cSAlex Williamson     memory_region_init_io(&dev->msix_pba_mmio, &msix_pba_mmio_ops, dev,
287d35e428cSAlex Williamson                           "msix-pba", pba_size);
2885a2c2029SAlex Williamson     memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio);
28902eb84d0SMichael S. Tsirkin 
29002eb84d0SMichael S. Tsirkin     return 0;
29102eb84d0SMichael S. Tsirkin }
29202eb84d0SMichael S. Tsirkin 
29353f94925SAlex Williamson int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
29453f94925SAlex Williamson                             uint8_t bar_nr)
29553f94925SAlex Williamson {
29653f94925SAlex Williamson     int ret;
29753f94925SAlex Williamson     char *name;
29853f94925SAlex Williamson 
29953f94925SAlex Williamson     /*
30053f94925SAlex Williamson      * Migration compatibility dictates that this remains a 4k
30153f94925SAlex Williamson      * BAR with the vector table in the lower half and PBA in
30253f94925SAlex Williamson      * the upper half.  Do not use these elsewhere!
30353f94925SAlex Williamson      */
30453f94925SAlex Williamson #define MSIX_EXCLUSIVE_BAR_SIZE 4096
3055a2c2029SAlex Williamson #define MSIX_EXCLUSIVE_BAR_TABLE_OFFSET 0
30653f94925SAlex Williamson #define MSIX_EXCLUSIVE_BAR_PBA_OFFSET (MSIX_EXCLUSIVE_BAR_SIZE / 2)
3075a2c2029SAlex Williamson #define MSIX_EXCLUSIVE_CAP_OFFSET 0
30853f94925SAlex Williamson 
30953f94925SAlex Williamson     if (nentries * PCI_MSIX_ENTRY_SIZE > MSIX_EXCLUSIVE_BAR_PBA_OFFSET) {
31053f94925SAlex Williamson         return -EINVAL;
31153f94925SAlex Williamson     }
31253f94925SAlex Williamson 
3135f893b4eSGerd Hoffmann     name = g_strdup_printf("%s-msix", dev->name);
31453f94925SAlex Williamson     memory_region_init(&dev->msix_exclusive_bar, name, MSIX_EXCLUSIVE_BAR_SIZE);
3155f893b4eSGerd Hoffmann     g_free(name);
31653f94925SAlex Williamson 
31753f94925SAlex Williamson     ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr,
3185a2c2029SAlex Williamson                     MSIX_EXCLUSIVE_BAR_TABLE_OFFSET, &dev->msix_exclusive_bar,
3195a2c2029SAlex Williamson                     bar_nr, MSIX_EXCLUSIVE_BAR_PBA_OFFSET,
3205a2c2029SAlex Williamson                     MSIX_EXCLUSIVE_CAP_OFFSET);
32153f94925SAlex Williamson     if (ret) {
32253f94925SAlex Williamson         memory_region_destroy(&dev->msix_exclusive_bar);
32353f94925SAlex Williamson         return ret;
32453f94925SAlex Williamson     }
32553f94925SAlex Williamson 
32653f94925SAlex Williamson     pci_register_bar(dev, bar_nr, PCI_BASE_ADDRESS_SPACE_MEMORY,
32753f94925SAlex Williamson                      &dev->msix_exclusive_bar);
32853f94925SAlex Williamson 
32953f94925SAlex Williamson     return 0;
33053f94925SAlex Williamson }
33153f94925SAlex Williamson 
33298304c84SMichael S. Tsirkin static void msix_free_irq_entries(PCIDevice *dev)
33398304c84SMichael S. Tsirkin {
33498304c84SMichael S. Tsirkin     int vector;
33598304c84SMichael S. Tsirkin 
33698304c84SMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
33798304c84SMichael S. Tsirkin         dev->msix_entry_used[vector] = 0;
33898304c84SMichael S. Tsirkin         msix_clr_pending(dev, vector);
33998304c84SMichael S. Tsirkin     }
34098304c84SMichael S. Tsirkin }
34198304c84SMichael S. Tsirkin 
3423cac001eSMichael S. Tsirkin static void msix_clear_all_vectors(PCIDevice *dev)
3433cac001eSMichael S. Tsirkin {
3443cac001eSMichael S. Tsirkin     int vector;
3453cac001eSMichael S. Tsirkin 
3463cac001eSMichael S. Tsirkin     for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
3473cac001eSMichael S. Tsirkin         msix_clr_pending(dev, vector);
3483cac001eSMichael S. Tsirkin     }
3493cac001eSMichael S. Tsirkin }
3503cac001eSMichael S. Tsirkin 
35102eb84d0SMichael S. Tsirkin /* Clean up resources for the device. */
352572992eeSAlex Williamson void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar)
35302eb84d0SMichael S. Tsirkin {
35444701ab7SJan Kiszka     if (!msix_present(dev)) {
355572992eeSAlex Williamson         return;
35644701ab7SJan Kiszka     }
35702eb84d0SMichael S. Tsirkin     pci_del_capability(dev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH);
35802eb84d0SMichael S. Tsirkin     dev->msix_cap = 0;
35902eb84d0SMichael S. Tsirkin     msix_free_irq_entries(dev);
36002eb84d0SMichael S. Tsirkin     dev->msix_entries_nr = 0;
3615a2c2029SAlex Williamson     memory_region_del_subregion(pba_bar, &dev->msix_pba_mmio);
362d35e428cSAlex Williamson     memory_region_destroy(&dev->msix_pba_mmio);
363d35e428cSAlex Williamson     g_free(dev->msix_pba);
364d35e428cSAlex Williamson     dev->msix_pba = NULL;
3655a2c2029SAlex Williamson     memory_region_del_subregion(table_bar, &dev->msix_table_mmio);
366d35e428cSAlex Williamson     memory_region_destroy(&dev->msix_table_mmio);
367d35e428cSAlex Williamson     g_free(dev->msix_table);
368d35e428cSAlex Williamson     dev->msix_table = NULL;
3697267c094SAnthony Liguori     g_free(dev->msix_entry_used);
37002eb84d0SMichael S. Tsirkin     dev->msix_entry_used = NULL;
37102eb84d0SMichael S. Tsirkin     dev->cap_present &= ~QEMU_PCI_CAP_MSIX;
37202eb84d0SMichael S. Tsirkin }
37302eb84d0SMichael S. Tsirkin 
37453f94925SAlex Williamson void msix_uninit_exclusive_bar(PCIDevice *dev)
37553f94925SAlex Williamson {
37653f94925SAlex Williamson     if (msix_present(dev)) {
3775a2c2029SAlex Williamson         msix_uninit(dev, &dev->msix_exclusive_bar, &dev->msix_exclusive_bar);
37853f94925SAlex Williamson         memory_region_destroy(&dev->msix_exclusive_bar);
37953f94925SAlex Williamson     }
38053f94925SAlex Williamson }
38153f94925SAlex Williamson 
38202eb84d0SMichael S. Tsirkin void msix_save(PCIDevice *dev, QEMUFile *f)
38302eb84d0SMichael S. Tsirkin {
3849a3e12c8SMichael S. Tsirkin     unsigned n = dev->msix_entries_nr;
3859a3e12c8SMichael S. Tsirkin 
38644701ab7SJan Kiszka     if (!msix_present(dev)) {
3879a3e12c8SMichael S. Tsirkin         return;
38872755a70SMichael S. Tsirkin     }
3899a3e12c8SMichael S. Tsirkin 
390d35e428cSAlex Williamson     qemu_put_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
391d35e428cSAlex Williamson     qemu_put_buffer(f, dev->msix_pba, (n + 7) / 8);
39202eb84d0SMichael S. Tsirkin }
39302eb84d0SMichael S. Tsirkin 
39402eb84d0SMichael S. Tsirkin /* Should be called after restoring the config space. */
39502eb84d0SMichael S. Tsirkin void msix_load(PCIDevice *dev, QEMUFile *f)
39602eb84d0SMichael S. Tsirkin {
39702eb84d0SMichael S. Tsirkin     unsigned n = dev->msix_entries_nr;
3982cdfe53cSJan Kiszka     unsigned int vector;
39902eb84d0SMichael S. Tsirkin 
40044701ab7SJan Kiszka     if (!msix_present(dev)) {
40102eb84d0SMichael S. Tsirkin         return;
40298846d73SBlue Swirl     }
40302eb84d0SMichael S. Tsirkin 
4043cac001eSMichael S. Tsirkin     msix_clear_all_vectors(dev);
405d35e428cSAlex Williamson     qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
406d35e428cSAlex Williamson     qemu_get_buffer(f, dev->msix_pba, (n + 7) / 8);
40750322249SMichael S. Tsirkin     msix_update_function_masked(dev);
4082cdfe53cSJan Kiszka 
4092cdfe53cSJan Kiszka     for (vector = 0; vector < n; vector++) {
4102cdfe53cSJan Kiszka         msix_handle_mask_update(dev, vector, true);
4112cdfe53cSJan Kiszka     }
41202eb84d0SMichael S. Tsirkin }
41302eb84d0SMichael S. Tsirkin 
41402eb84d0SMichael S. Tsirkin /* Does device support MSI-X? */
41502eb84d0SMichael S. Tsirkin int msix_present(PCIDevice *dev)
41602eb84d0SMichael S. Tsirkin {
41702eb84d0SMichael S. Tsirkin     return dev->cap_present & QEMU_PCI_CAP_MSIX;
41802eb84d0SMichael S. Tsirkin }
41902eb84d0SMichael S. Tsirkin 
42002eb84d0SMichael S. Tsirkin /* Is MSI-X enabled? */
42102eb84d0SMichael S. Tsirkin int msix_enabled(PCIDevice *dev)
42202eb84d0SMichael S. Tsirkin {
42302eb84d0SMichael S. Tsirkin     return (dev->cap_present & QEMU_PCI_CAP_MSIX) &&
4242760952bSMichael S. Tsirkin         (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
42502eb84d0SMichael S. Tsirkin          MSIX_ENABLE_MASK);
42602eb84d0SMichael S. Tsirkin }
42702eb84d0SMichael S. Tsirkin 
42802eb84d0SMichael S. Tsirkin /* Send an MSI-X message */
42902eb84d0SMichael S. Tsirkin void msix_notify(PCIDevice *dev, unsigned vector)
43002eb84d0SMichael S. Tsirkin {
431bc4caf49SJan Kiszka     MSIMessage msg;
43202eb84d0SMichael S. Tsirkin 
43302eb84d0SMichael S. Tsirkin     if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector])
43402eb84d0SMichael S. Tsirkin         return;
43502eb84d0SMichael S. Tsirkin     if (msix_is_masked(dev, vector)) {
43602eb84d0SMichael S. Tsirkin         msix_set_pending(dev, vector);
43702eb84d0SMichael S. Tsirkin         return;
43802eb84d0SMichael S. Tsirkin     }
43902eb84d0SMichael S. Tsirkin 
440bc4caf49SJan Kiszka     msg = msix_get_message(dev, vector);
441bc4caf49SJan Kiszka 
442bc4caf49SJan Kiszka     stl_le_phys(msg.address, msg.data);
44302eb84d0SMichael S. Tsirkin }
44402eb84d0SMichael S. Tsirkin 
44502eb84d0SMichael S. Tsirkin void msix_reset(PCIDevice *dev)
44602eb84d0SMichael S. Tsirkin {
44744701ab7SJan Kiszka     if (!msix_present(dev)) {
44802eb84d0SMichael S. Tsirkin         return;
44944701ab7SJan Kiszka     }
4503cac001eSMichael S. Tsirkin     msix_clear_all_vectors(dev);
4512760952bSMichael S. Tsirkin     dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &=
4522760952bSMichael S. Tsirkin 	    ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET];
453d35e428cSAlex Williamson     memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
454d35e428cSAlex Williamson     memset(dev->msix_pba, 0, QEMU_ALIGN_UP(dev->msix_entries_nr, 64) / 8);
455ae1be0bbSMichael S. Tsirkin     msix_mask_all(dev, dev->msix_entries_nr);
45602eb84d0SMichael S. Tsirkin }
45702eb84d0SMichael S. Tsirkin 
45802eb84d0SMichael S. Tsirkin /* PCI spec suggests that devices make it possible for software to configure
45902eb84d0SMichael S. Tsirkin  * less vectors than supported by the device, but does not specify a standard
46002eb84d0SMichael S. Tsirkin  * mechanism for devices to do so.
46102eb84d0SMichael S. Tsirkin  *
46202eb84d0SMichael S. Tsirkin  * We support this by asking devices to declare vectors software is going to
46302eb84d0SMichael S. Tsirkin  * actually use, and checking this on the notification path. Devices that
46402eb84d0SMichael S. Tsirkin  * don't want to follow the spec suggestion can declare all vectors as used. */
46502eb84d0SMichael S. Tsirkin 
46602eb84d0SMichael S. Tsirkin /* Mark vector as used. */
46702eb84d0SMichael S. Tsirkin int msix_vector_use(PCIDevice *dev, unsigned vector)
46802eb84d0SMichael S. Tsirkin {
46902eb84d0SMichael S. Tsirkin     if (vector >= dev->msix_entries_nr)
47002eb84d0SMichael S. Tsirkin         return -EINVAL;
47102eb84d0SMichael S. Tsirkin     dev->msix_entry_used[vector]++;
47202eb84d0SMichael S. Tsirkin     return 0;
47302eb84d0SMichael S. Tsirkin }
47402eb84d0SMichael S. Tsirkin 
47502eb84d0SMichael S. Tsirkin /* Mark vector as unused. */
47602eb84d0SMichael S. Tsirkin void msix_vector_unuse(PCIDevice *dev, unsigned vector)
47702eb84d0SMichael S. Tsirkin {
47898304c84SMichael S. Tsirkin     if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) {
47998304c84SMichael S. Tsirkin         return;
48098304c84SMichael S. Tsirkin     }
48198304c84SMichael S. Tsirkin     if (--dev->msix_entry_used[vector]) {
48298304c84SMichael S. Tsirkin         return;
48398304c84SMichael S. Tsirkin     }
48498304c84SMichael S. Tsirkin     msix_clr_pending(dev, vector);
48502eb84d0SMichael S. Tsirkin }
486b5f28bcaSMichael S. Tsirkin 
487b5f28bcaSMichael S. Tsirkin void msix_unuse_all_vectors(PCIDevice *dev)
488b5f28bcaSMichael S. Tsirkin {
48944701ab7SJan Kiszka     if (!msix_present(dev)) {
490b5f28bcaSMichael S. Tsirkin         return;
49144701ab7SJan Kiszka     }
492b5f28bcaSMichael S. Tsirkin     msix_free_irq_entries(dev);
493b5f28bcaSMichael S. Tsirkin }
4942cdfe53cSJan Kiszka 
495cb697aaaSJan Kiszka unsigned int msix_nr_vectors_allocated(const PCIDevice *dev)
496cb697aaaSJan Kiszka {
497cb697aaaSJan Kiszka     return dev->msix_entries_nr;
498cb697aaaSJan Kiszka }
499cb697aaaSJan Kiszka 
5002cdfe53cSJan Kiszka static int msix_set_notifier_for_vector(PCIDevice *dev, unsigned int vector)
5012cdfe53cSJan Kiszka {
5022cdfe53cSJan Kiszka     MSIMessage msg;
5032cdfe53cSJan Kiszka 
5042cdfe53cSJan Kiszka     if (msix_is_masked(dev, vector)) {
5052cdfe53cSJan Kiszka         return 0;
5062cdfe53cSJan Kiszka     }
5072cdfe53cSJan Kiszka     msg = msix_get_message(dev, vector);
5082cdfe53cSJan Kiszka     return dev->msix_vector_use_notifier(dev, vector, msg);
5092cdfe53cSJan Kiszka }
5102cdfe53cSJan Kiszka 
5112cdfe53cSJan Kiszka static void msix_unset_notifier_for_vector(PCIDevice *dev, unsigned int vector)
5122cdfe53cSJan Kiszka {
5132cdfe53cSJan Kiszka     if (msix_is_masked(dev, vector)) {
5142cdfe53cSJan Kiszka         return;
5152cdfe53cSJan Kiszka     }
5162cdfe53cSJan Kiszka     dev->msix_vector_release_notifier(dev, vector);
5172cdfe53cSJan Kiszka }
5182cdfe53cSJan Kiszka 
5192cdfe53cSJan Kiszka int msix_set_vector_notifiers(PCIDevice *dev,
5202cdfe53cSJan Kiszka                               MSIVectorUseNotifier use_notifier,
521bbef882cSMichael S. Tsirkin                               MSIVectorReleaseNotifier release_notifier,
522bbef882cSMichael S. Tsirkin                               MSIVectorPollNotifier poll_notifier)
5232cdfe53cSJan Kiszka {
5242cdfe53cSJan Kiszka     int vector, ret;
5252cdfe53cSJan Kiszka 
5262cdfe53cSJan Kiszka     assert(use_notifier && release_notifier);
5272cdfe53cSJan Kiszka 
5282cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = use_notifier;
5292cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = release_notifier;
530bbef882cSMichael S. Tsirkin     dev->msix_vector_poll_notifier = poll_notifier;
5312cdfe53cSJan Kiszka 
5322cdfe53cSJan Kiszka     if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
5332cdfe53cSJan Kiszka         (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) {
5342cdfe53cSJan Kiszka         for (vector = 0; vector < dev->msix_entries_nr; vector++) {
5352cdfe53cSJan Kiszka             ret = msix_set_notifier_for_vector(dev, vector);
5362cdfe53cSJan Kiszka             if (ret < 0) {
5372cdfe53cSJan Kiszka                 goto undo;
5382cdfe53cSJan Kiszka             }
5392cdfe53cSJan Kiszka         }
5402cdfe53cSJan Kiszka     }
541bbef882cSMichael S. Tsirkin     if (dev->msix_vector_poll_notifier) {
542bbef882cSMichael S. Tsirkin         dev->msix_vector_poll_notifier(dev, 0, dev->msix_entries_nr);
543bbef882cSMichael S. Tsirkin     }
5442cdfe53cSJan Kiszka     return 0;
5452cdfe53cSJan Kiszka 
5462cdfe53cSJan Kiszka undo:
5472cdfe53cSJan Kiszka     while (--vector >= 0) {
5482cdfe53cSJan Kiszka         msix_unset_notifier_for_vector(dev, vector);
5492cdfe53cSJan Kiszka     }
5502cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = NULL;
5512cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = NULL;
5522cdfe53cSJan Kiszka     return ret;
5532cdfe53cSJan Kiszka }
5542cdfe53cSJan Kiszka 
5552cdfe53cSJan Kiszka void msix_unset_vector_notifiers(PCIDevice *dev)
5562cdfe53cSJan Kiszka {
5572cdfe53cSJan Kiszka     int vector;
5582cdfe53cSJan Kiszka 
5592cdfe53cSJan Kiszka     assert(dev->msix_vector_use_notifier &&
5602cdfe53cSJan Kiszka            dev->msix_vector_release_notifier);
5612cdfe53cSJan Kiszka 
5622cdfe53cSJan Kiszka     if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
5632cdfe53cSJan Kiszka         (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) {
5642cdfe53cSJan Kiszka         for (vector = 0; vector < dev->msix_entries_nr; vector++) {
5652cdfe53cSJan Kiszka             msix_unset_notifier_for_vector(dev, vector);
5662cdfe53cSJan Kiszka         }
5672cdfe53cSJan Kiszka     }
5682cdfe53cSJan Kiszka     dev->msix_vector_use_notifier = NULL;
5692cdfe53cSJan Kiszka     dev->msix_vector_release_notifier = NULL;
570bbef882cSMichael S. Tsirkin     dev->msix_vector_poll_notifier = NULL;
5712cdfe53cSJan Kiszka }
572*340b50c7SGerd Hoffmann 
573*340b50c7SGerd Hoffmann static void put_msix_state(QEMUFile *f, void *pv, size_t size)
574*340b50c7SGerd Hoffmann {
575*340b50c7SGerd Hoffmann     msix_save(pv, f);
576*340b50c7SGerd Hoffmann }
577*340b50c7SGerd Hoffmann 
578*340b50c7SGerd Hoffmann static int get_msix_state(QEMUFile *f, void *pv, size_t size)
579*340b50c7SGerd Hoffmann {
580*340b50c7SGerd Hoffmann     msix_load(pv, f);
581*340b50c7SGerd Hoffmann     return 0;
582*340b50c7SGerd Hoffmann }
583*340b50c7SGerd Hoffmann 
584*340b50c7SGerd Hoffmann static VMStateInfo vmstate_info_msix = {
585*340b50c7SGerd Hoffmann     .name = "msix state",
586*340b50c7SGerd Hoffmann     .get  = get_msix_state,
587*340b50c7SGerd Hoffmann     .put  = put_msix_state,
588*340b50c7SGerd Hoffmann };
589*340b50c7SGerd Hoffmann 
590*340b50c7SGerd Hoffmann const VMStateDescription vmstate_msix = {
591*340b50c7SGerd Hoffmann     .name = "msix",
592*340b50c7SGerd Hoffmann     .fields = (VMStateField[]) {
593*340b50c7SGerd Hoffmann         {
594*340b50c7SGerd Hoffmann             .name         = "msix",
595*340b50c7SGerd Hoffmann             .version_id   = 0,
596*340b50c7SGerd Hoffmann             .field_exists = NULL,
597*340b50c7SGerd Hoffmann             .size         = 0,   /* ouch */
598*340b50c7SGerd Hoffmann             .info         = &vmstate_info_msix,
599*340b50c7SGerd Hoffmann             .flags        = VMS_SINGLE,
600*340b50c7SGerd Hoffmann             .offset       = 0,
601*340b50c7SGerd Hoffmann         },
602*340b50c7SGerd Hoffmann         VMSTATE_END_OF_LIST()
603*340b50c7SGerd Hoffmann     }
604*340b50c7SGerd Hoffmann };
605