xref: /openbmc/qemu/hw/pci/msi.c (revision 7489f7f3f81dcb776df8c1b9a9db281fc21bf05f)
1315a1350SMichael S. Tsirkin /*
2315a1350SMichael S. Tsirkin  * msi.c
3315a1350SMichael S. Tsirkin  *
4315a1350SMichael S. Tsirkin  * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
5315a1350SMichael S. Tsirkin  *                    VA Linux Systems Japan K.K.
6315a1350SMichael S. Tsirkin  *
7315a1350SMichael S. Tsirkin  * This program is free software; you can redistribute it and/or modify
8315a1350SMichael S. Tsirkin  * it under the terms of the GNU General Public License as published by
9315a1350SMichael S. Tsirkin  * the Free Software Foundation; either version 2 of the License, or
10315a1350SMichael S. Tsirkin  * (at your option) any later version.
11315a1350SMichael S. Tsirkin 
12315a1350SMichael S. Tsirkin  * This program is distributed in the hope that it will be useful,
13315a1350SMichael S. Tsirkin  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14315a1350SMichael S. Tsirkin  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15315a1350SMichael S. Tsirkin  * GNU General Public License for more details.
16315a1350SMichael S. Tsirkin 
17315a1350SMichael S. Tsirkin  * You should have received a copy of the GNU General Public License along
18315a1350SMichael S. Tsirkin  * with this program; if not, see <http://www.gnu.org/licenses/>.
19315a1350SMichael S. Tsirkin  */
20315a1350SMichael S. Tsirkin 
2197d5408fSPeter Maydell #include "qemu/osdep.h"
22c759b24fSMichael S. Tsirkin #include "hw/pci/msi.h"
23428c3eceSStefano Stabellini #include "hw/xen/xen.h"
241de7afc9SPaolo Bonzini #include "qemu/range.h"
251108b2f8SCao jin #include "qapi/error.h"
26*06c83376SPhilippe Mathieu-Daudé #include "sysemu/xen.h"
27315a1350SMichael S. Tsirkin 
286096cf78SDavid Woodhouse #include "hw/i386/kvm/xen_evtchn.h"
296096cf78SDavid Woodhouse 
30315a1350SMichael S. Tsirkin /* PCI_MSI_ADDRESS_LO */
31315a1350SMichael S. Tsirkin #define PCI_MSI_ADDRESS_LO_MASK         (~0x3)
32315a1350SMichael S. Tsirkin 
33315a1350SMichael S. Tsirkin /* If we get rid of cap allocator, we won't need those. */
34315a1350SMichael S. Tsirkin #define PCI_MSI_32_SIZEOF       0x0a
35315a1350SMichael S. Tsirkin #define PCI_MSI_64_SIZEOF       0x0e
36315a1350SMichael S. Tsirkin #define PCI_MSI_32M_SIZEOF      0x14
37315a1350SMichael S. Tsirkin #define PCI_MSI_64M_SIZEOF      0x18
38315a1350SMichael S. Tsirkin 
39315a1350SMichael S. Tsirkin #define PCI_MSI_VECTORS_MAX     32
40315a1350SMichael S. Tsirkin 
41226419d6SMichael S. Tsirkin /*
42226419d6SMichael S. Tsirkin  * Flag for interrupt controllers to declare broken MSI/MSI-X support.
43226419d6SMichael S. Tsirkin  * values: false - broken; true - non-broken.
44226419d6SMichael S. Tsirkin  *
45226419d6SMichael S. Tsirkin  * Setting this flag to false will remove MSI/MSI-X capability from all devices.
46226419d6SMichael S. Tsirkin  *
47cb8d4c8fSStefan Weil  * It is preferable for controllers to set this to true (non-broken) even if
48226419d6SMichael S. Tsirkin  * they do not actually support MSI/MSI-X: guests normally probe the controller
49226419d6SMichael S. Tsirkin  * type and do not attempt to enable MSI/MSI-X with interrupt controllers not
50226419d6SMichael S. Tsirkin  * supporting such, so removing the capability is not required, and
51226419d6SMichael S. Tsirkin  * it seems cleaner to have a given device look the same for all boards.
52226419d6SMichael S. Tsirkin  *
53226419d6SMichael S. Tsirkin  * TODO: some existing controllers violate the above rule. Identify and fix them.
54226419d6SMichael S. Tsirkin  */
55226419d6SMichael S. Tsirkin bool msi_nonbroken;
56315a1350SMichael S. Tsirkin 
57315a1350SMichael S. Tsirkin /* If we get rid of cap allocator, we won't need this. */
msi_cap_sizeof(uint16_t flags)58315a1350SMichael S. Tsirkin static inline uint8_t msi_cap_sizeof(uint16_t flags)
59315a1350SMichael S. Tsirkin {
60315a1350SMichael S. Tsirkin     switch (flags & (PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT)) {
61315a1350SMichael S. Tsirkin     case PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT:
62315a1350SMichael S. Tsirkin         return PCI_MSI_64M_SIZEOF;
63315a1350SMichael S. Tsirkin     case PCI_MSI_FLAGS_64BIT:
64315a1350SMichael S. Tsirkin         return PCI_MSI_64_SIZEOF;
65315a1350SMichael S. Tsirkin     case PCI_MSI_FLAGS_MASKBIT:
66315a1350SMichael S. Tsirkin         return PCI_MSI_32M_SIZEOF;
67315a1350SMichael S. Tsirkin     case 0:
68315a1350SMichael S. Tsirkin         return PCI_MSI_32_SIZEOF;
69315a1350SMichael S. Tsirkin     default:
70315a1350SMichael S. Tsirkin         abort();
71315a1350SMichael S. Tsirkin         break;
72315a1350SMichael S. Tsirkin     }
73315a1350SMichael S. Tsirkin     return 0;
74315a1350SMichael S. Tsirkin }
75315a1350SMichael S. Tsirkin 
76315a1350SMichael S. Tsirkin //#define MSI_DEBUG
77315a1350SMichael S. Tsirkin 
78315a1350SMichael S. Tsirkin #ifdef MSI_DEBUG
79315a1350SMichael S. Tsirkin # define MSI_DPRINTF(fmt, ...)                                          \
80315a1350SMichael S. Tsirkin     fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__)
81315a1350SMichael S. Tsirkin #else
82315a1350SMichael S. Tsirkin # define MSI_DPRINTF(fmt, ...)  do { } while (0)
83315a1350SMichael S. Tsirkin #endif
84315a1350SMichael S. Tsirkin #define MSI_DEV_PRINTF(dev, fmt, ...)                                   \
85315a1350SMichael S. Tsirkin     MSI_DPRINTF("%s:%x " fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__)
86315a1350SMichael S. Tsirkin 
msi_nr_vectors(uint16_t flags)87315a1350SMichael S. Tsirkin static inline unsigned int msi_nr_vectors(uint16_t flags)
88315a1350SMichael S. Tsirkin {
89315a1350SMichael S. Tsirkin     return 1U <<
90786a4ea8SStefan Hajnoczi         ((flags & PCI_MSI_FLAGS_QSIZE) >> ctz32(PCI_MSI_FLAGS_QSIZE));
91315a1350SMichael S. Tsirkin }
92315a1350SMichael S. Tsirkin 
msi_flags_off(const PCIDevice * dev)93315a1350SMichael S. Tsirkin static inline uint8_t msi_flags_off(const PCIDevice* dev)
94315a1350SMichael S. Tsirkin {
95315a1350SMichael S. Tsirkin     return dev->msi_cap + PCI_MSI_FLAGS;
96315a1350SMichael S. Tsirkin }
97315a1350SMichael S. Tsirkin 
msi_address_lo_off(const PCIDevice * dev)98315a1350SMichael S. Tsirkin static inline uint8_t msi_address_lo_off(const PCIDevice* dev)
99315a1350SMichael S. Tsirkin {
100315a1350SMichael S. Tsirkin     return dev->msi_cap + PCI_MSI_ADDRESS_LO;
101315a1350SMichael S. Tsirkin }
102315a1350SMichael S. Tsirkin 
msi_address_hi_off(const PCIDevice * dev)103315a1350SMichael S. Tsirkin static inline uint8_t msi_address_hi_off(const PCIDevice* dev)
104315a1350SMichael S. Tsirkin {
105315a1350SMichael S. Tsirkin     return dev->msi_cap + PCI_MSI_ADDRESS_HI;
106315a1350SMichael S. Tsirkin }
107315a1350SMichael S. Tsirkin 
msi_data_off(const PCIDevice * dev,bool msi64bit)108315a1350SMichael S. Tsirkin static inline uint8_t msi_data_off(const PCIDevice* dev, bool msi64bit)
109315a1350SMichael S. Tsirkin {
110315a1350SMichael S. Tsirkin     return dev->msi_cap + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32);
111315a1350SMichael S. Tsirkin }
112315a1350SMichael S. Tsirkin 
msi_mask_off(const PCIDevice * dev,bool msi64bit)113315a1350SMichael S. Tsirkin static inline uint8_t msi_mask_off(const PCIDevice* dev, bool msi64bit)
114315a1350SMichael S. Tsirkin {
115315a1350SMichael S. Tsirkin     return dev->msi_cap + (msi64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32);
116315a1350SMichael S. Tsirkin }
117315a1350SMichael S. Tsirkin 
msi_pending_off(const PCIDevice * dev,bool msi64bit)118315a1350SMichael S. Tsirkin static inline uint8_t msi_pending_off(const PCIDevice* dev, bool msi64bit)
119315a1350SMichael S. Tsirkin {
120315a1350SMichael S. Tsirkin     return dev->msi_cap + (msi64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32);
121315a1350SMichael S. Tsirkin }
122315a1350SMichael S. Tsirkin 
123315a1350SMichael S. Tsirkin /*
124315a1350SMichael S. Tsirkin  * Special API for POWER to configure the vectors through
125315a1350SMichael S. Tsirkin  * a side channel. Should never be used by devices.
126315a1350SMichael S. Tsirkin  */
msi_set_message(PCIDevice * dev,MSIMessage msg)127315a1350SMichael S. Tsirkin void msi_set_message(PCIDevice *dev, MSIMessage msg)
128315a1350SMichael S. Tsirkin {
129315a1350SMichael S. Tsirkin     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
130315a1350SMichael S. Tsirkin     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
131315a1350SMichael S. Tsirkin 
132315a1350SMichael S. Tsirkin     if (msi64bit) {
133315a1350SMichael S. Tsirkin         pci_set_quad(dev->config + msi_address_lo_off(dev), msg.address);
134315a1350SMichael S. Tsirkin     } else {
135315a1350SMichael S. Tsirkin         pci_set_long(dev->config + msi_address_lo_off(dev), msg.address);
136315a1350SMichael S. Tsirkin     }
137315a1350SMichael S. Tsirkin     pci_set_word(dev->config + msi_data_off(dev, msi64bit), msg.data);
138315a1350SMichael S. Tsirkin }
139315a1350SMichael S. Tsirkin 
msi_prepare_message(PCIDevice * dev,unsigned int vector)14008cf3dc6SJagannathan Raman static MSIMessage msi_prepare_message(PCIDevice *dev, unsigned int vector)
141315a1350SMichael S. Tsirkin {
142315a1350SMichael S. Tsirkin     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
143315a1350SMichael S. Tsirkin     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
144315a1350SMichael S. Tsirkin     unsigned int nr_vectors = msi_nr_vectors(flags);
145315a1350SMichael S. Tsirkin     MSIMessage msg;
146315a1350SMichael S. Tsirkin 
147315a1350SMichael S. Tsirkin     assert(vector < nr_vectors);
148315a1350SMichael S. Tsirkin 
149315a1350SMichael S. Tsirkin     if (msi64bit) {
150315a1350SMichael S. Tsirkin         msg.address = pci_get_quad(dev->config + msi_address_lo_off(dev));
151315a1350SMichael S. Tsirkin     } else {
152315a1350SMichael S. Tsirkin         msg.address = pci_get_long(dev->config + msi_address_lo_off(dev));
153315a1350SMichael S. Tsirkin     }
154315a1350SMichael S. Tsirkin 
155315a1350SMichael S. Tsirkin     /* upper bit 31:16 is zero */
156315a1350SMichael S. Tsirkin     msg.data = pci_get_word(dev->config + msi_data_off(dev, msi64bit));
157315a1350SMichael S. Tsirkin     if (nr_vectors > 1) {
158315a1350SMichael S. Tsirkin         msg.data &= ~(nr_vectors - 1);
159315a1350SMichael S. Tsirkin         msg.data |= vector;
160315a1350SMichael S. Tsirkin     }
161315a1350SMichael S. Tsirkin 
162315a1350SMichael S. Tsirkin     return msg;
163315a1350SMichael S. Tsirkin }
164315a1350SMichael S. Tsirkin 
msi_get_message(PCIDevice * dev,unsigned int vector)16508cf3dc6SJagannathan Raman MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector)
16608cf3dc6SJagannathan Raman {
16708cf3dc6SJagannathan Raman     return dev->msi_prepare_message(dev, vector);
16808cf3dc6SJagannathan Raman }
16908cf3dc6SJagannathan Raman 
msi_enabled(const PCIDevice * dev)170315a1350SMichael S. Tsirkin bool msi_enabled(const PCIDevice *dev)
171315a1350SMichael S. Tsirkin {
172315a1350SMichael S. Tsirkin     return msi_present(dev) &&
173315a1350SMichael S. Tsirkin         (pci_get_word(dev->config + msi_flags_off(dev)) &
174315a1350SMichael S. Tsirkin          PCI_MSI_FLAGS_ENABLE);
175315a1350SMichael S. Tsirkin }
176315a1350SMichael S. Tsirkin 
17752ea63deSCao jin /*
17852ea63deSCao jin  * Make PCI device @dev MSI-capable.
17952ea63deSCao jin  * Non-zero @offset puts capability MSI at that offset in PCI config
18052ea63deSCao jin  * space.
18152ea63deSCao jin  * @nr_vectors is the number of MSI vectors (1, 2, 4, 8, 16 or 32).
18252ea63deSCao jin  * If @msi64bit, make the device capable of sending a 64-bit message
18352ea63deSCao jin  * address.
18452ea63deSCao jin  * If @msi_per_vector_mask, make the device support per-vector masking.
1851108b2f8SCao jin  * @errp is for returning errors.
1861108b2f8SCao jin  * Return 0 on success; set @errp and return -errno on error.
18752ea63deSCao jin  *
18852ea63deSCao jin  * -ENOTSUP means lacking msi support for a msi-capable platform.
18952ea63deSCao jin  * -EINVAL means capability overlap, happens when @offset is non-zero,
19052ea63deSCao jin  *  also means a programming error, except device assignment, which can check
19152ea63deSCao jin  *  if a real HW is broken.
19252ea63deSCao jin  */
msi_init(struct PCIDevice * dev,uint8_t offset,unsigned int nr_vectors,bool msi64bit,bool msi_per_vector_mask,Error ** errp)193315a1350SMichael S. Tsirkin int msi_init(struct PCIDevice *dev, uint8_t offset,
1941108b2f8SCao jin              unsigned int nr_vectors, bool msi64bit,
1951108b2f8SCao jin              bool msi_per_vector_mask, Error **errp)
196315a1350SMichael S. Tsirkin {
197315a1350SMichael S. Tsirkin     unsigned int vectors_order;
198315a1350SMichael S. Tsirkin     uint16_t flags;
199315a1350SMichael S. Tsirkin     uint8_t cap_size;
200315a1350SMichael S. Tsirkin     int config_offset;
201315a1350SMichael S. Tsirkin 
202226419d6SMichael S. Tsirkin     if (!msi_nonbroken) {
2031108b2f8SCao jin         error_setg(errp, "MSI is not supported by interrupt controller");
204315a1350SMichael S. Tsirkin         return -ENOTSUP;
205315a1350SMichael S. Tsirkin     }
206315a1350SMichael S. Tsirkin 
207315a1350SMichael S. Tsirkin     MSI_DEV_PRINTF(dev,
208315a1350SMichael S. Tsirkin                    "init offset: 0x%"PRIx8" vector: %"PRId8
209315a1350SMichael S. Tsirkin                    " 64bit %d mask %d\n",
210315a1350SMichael S. Tsirkin                    offset, nr_vectors, msi64bit, msi_per_vector_mask);
211315a1350SMichael S. Tsirkin 
212315a1350SMichael S. Tsirkin     assert(!(nr_vectors & (nr_vectors - 1)));   /* power of 2 */
213315a1350SMichael S. Tsirkin     assert(nr_vectors > 0);
214315a1350SMichael S. Tsirkin     assert(nr_vectors <= PCI_MSI_VECTORS_MAX);
215315a1350SMichael S. Tsirkin     /* the nr of MSI vectors is up to 32 */
216786a4ea8SStefan Hajnoczi     vectors_order = ctz32(nr_vectors);
217315a1350SMichael S. Tsirkin 
218786a4ea8SStefan Hajnoczi     flags = vectors_order << ctz32(PCI_MSI_FLAGS_QMASK);
219315a1350SMichael S. Tsirkin     if (msi64bit) {
220315a1350SMichael S. Tsirkin         flags |= PCI_MSI_FLAGS_64BIT;
221315a1350SMichael S. Tsirkin     }
222315a1350SMichael S. Tsirkin     if (msi_per_vector_mask) {
223315a1350SMichael S. Tsirkin         flags |= PCI_MSI_FLAGS_MASKBIT;
224315a1350SMichael S. Tsirkin     }
225315a1350SMichael S. Tsirkin 
226315a1350SMichael S. Tsirkin     cap_size = msi_cap_sizeof(flags);
22727841278SMao Zhongyi     config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset,
2281108b2f8SCao jin                                         cap_size, errp);
229315a1350SMichael S. Tsirkin     if (config_offset < 0) {
230315a1350SMichael S. Tsirkin         return config_offset;
231315a1350SMichael S. Tsirkin     }
232315a1350SMichael S. Tsirkin 
233315a1350SMichael S. Tsirkin     dev->msi_cap = config_offset;
234315a1350SMichael S. Tsirkin     dev->cap_present |= QEMU_PCI_CAP_MSI;
235315a1350SMichael S. Tsirkin 
236315a1350SMichael S. Tsirkin     pci_set_word(dev->config + msi_flags_off(dev), flags);
237315a1350SMichael S. Tsirkin     pci_set_word(dev->wmask + msi_flags_off(dev),
238315a1350SMichael S. Tsirkin                  PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
239315a1350SMichael S. Tsirkin     pci_set_long(dev->wmask + msi_address_lo_off(dev),
240315a1350SMichael S. Tsirkin                  PCI_MSI_ADDRESS_LO_MASK);
241315a1350SMichael S. Tsirkin     if (msi64bit) {
242315a1350SMichael S. Tsirkin         pci_set_long(dev->wmask + msi_address_hi_off(dev), 0xffffffff);
243315a1350SMichael S. Tsirkin     }
244315a1350SMichael S. Tsirkin     pci_set_word(dev->wmask + msi_data_off(dev, msi64bit), 0xffff);
245315a1350SMichael S. Tsirkin 
246315a1350SMichael S. Tsirkin     if (msi_per_vector_mask) {
247315a1350SMichael S. Tsirkin         /* Make mask bits 0 to nr_vectors - 1 writable. */
248315a1350SMichael S. Tsirkin         pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit),
249315a1350SMichael S. Tsirkin                      0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors));
250315a1350SMichael S. Tsirkin     }
2512cbb1a68SCao jin 
25208cf3dc6SJagannathan Raman     dev->msi_prepare_message = msi_prepare_message;
25308cf3dc6SJagannathan Raman 
2542cbb1a68SCao jin     return 0;
255315a1350SMichael S. Tsirkin }
256315a1350SMichael S. Tsirkin 
msi_uninit(struct PCIDevice * dev)257315a1350SMichael S. Tsirkin void msi_uninit(struct PCIDevice *dev)
258315a1350SMichael S. Tsirkin {
259315a1350SMichael S. Tsirkin     uint16_t flags;
260315a1350SMichael S. Tsirkin     uint8_t cap_size;
261315a1350SMichael S. Tsirkin 
262315a1350SMichael S. Tsirkin     if (!msi_present(dev)) {
263315a1350SMichael S. Tsirkin         return;
264315a1350SMichael S. Tsirkin     }
265315a1350SMichael S. Tsirkin     flags = pci_get_word(dev->config + msi_flags_off(dev));
266315a1350SMichael S. Tsirkin     cap_size = msi_cap_sizeof(flags);
267315a1350SMichael S. Tsirkin     pci_del_capability(dev, PCI_CAP_ID_MSI, cap_size);
268315a1350SMichael S. Tsirkin     dev->cap_present &= ~QEMU_PCI_CAP_MSI;
26908cf3dc6SJagannathan Raman     dev->msi_prepare_message = NULL;
270315a1350SMichael S. Tsirkin 
271315a1350SMichael S. Tsirkin     MSI_DEV_PRINTF(dev, "uninit\n");
272315a1350SMichael S. Tsirkin }
273315a1350SMichael S. Tsirkin 
msi_reset(PCIDevice * dev)274315a1350SMichael S. Tsirkin void msi_reset(PCIDevice *dev)
275315a1350SMichael S. Tsirkin {
276315a1350SMichael S. Tsirkin     uint16_t flags;
277315a1350SMichael S. Tsirkin     bool msi64bit;
278315a1350SMichael S. Tsirkin 
279315a1350SMichael S. Tsirkin     if (!msi_present(dev)) {
280315a1350SMichael S. Tsirkin         return;
281315a1350SMichael S. Tsirkin     }
282315a1350SMichael S. Tsirkin 
283315a1350SMichael S. Tsirkin     flags = pci_get_word(dev->config + msi_flags_off(dev));
284315a1350SMichael S. Tsirkin     flags &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
285315a1350SMichael S. Tsirkin     msi64bit = flags & PCI_MSI_FLAGS_64BIT;
286315a1350SMichael S. Tsirkin 
287315a1350SMichael S. Tsirkin     pci_set_word(dev->config + msi_flags_off(dev), flags);
288315a1350SMichael S. Tsirkin     pci_set_long(dev->config + msi_address_lo_off(dev), 0);
289315a1350SMichael S. Tsirkin     if (msi64bit) {
290315a1350SMichael S. Tsirkin         pci_set_long(dev->config + msi_address_hi_off(dev), 0);
291315a1350SMichael S. Tsirkin     }
292315a1350SMichael S. Tsirkin     pci_set_word(dev->config + msi_data_off(dev, msi64bit), 0);
293315a1350SMichael S. Tsirkin     if (flags & PCI_MSI_FLAGS_MASKBIT) {
294315a1350SMichael S. Tsirkin         pci_set_long(dev->config + msi_mask_off(dev, msi64bit), 0);
295315a1350SMichael S. Tsirkin         pci_set_long(dev->config + msi_pending_off(dev, msi64bit), 0);
296315a1350SMichael S. Tsirkin     }
297315a1350SMichael S. Tsirkin     MSI_DEV_PRINTF(dev, "reset\n");
298315a1350SMichael S. Tsirkin }
299315a1350SMichael S. Tsirkin 
msi_is_masked(const PCIDevice * dev,unsigned int vector)300afa26eccSPeter Xu bool msi_is_masked(const PCIDevice *dev, unsigned int vector)
301315a1350SMichael S. Tsirkin {
302315a1350SMichael S. Tsirkin     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
303428c3eceSStefano Stabellini     uint32_t mask, data;
304428c3eceSStefano Stabellini     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
305315a1350SMichael S. Tsirkin     assert(vector < PCI_MSI_VECTORS_MAX);
306315a1350SMichael S. Tsirkin 
307315a1350SMichael S. Tsirkin     if (!(flags & PCI_MSI_FLAGS_MASKBIT)) {
308315a1350SMichael S. Tsirkin         return false;
309315a1350SMichael S. Tsirkin     }
310315a1350SMichael S. Tsirkin 
311428c3eceSStefano Stabellini     data = pci_get_word(dev->config + msi_data_off(dev, msi64bit));
312*06c83376SPhilippe Mathieu-Daudé     if (xen_enabled() && xen_is_pirq_msi(data)) {
313428c3eceSStefano Stabellini         return false;
314428c3eceSStefano Stabellini     }
315428c3eceSStefano Stabellini 
316315a1350SMichael S. Tsirkin     mask = pci_get_long(dev->config +
317315a1350SMichael S. Tsirkin                         msi_mask_off(dev, flags & PCI_MSI_FLAGS_64BIT));
318315a1350SMichael S. Tsirkin     return mask & (1U << vector);
319315a1350SMichael S. Tsirkin }
320315a1350SMichael S. Tsirkin 
msi_set_mask(PCIDevice * dev,int vector,bool mask,Error ** errp)32108cf3dc6SJagannathan Raman void msi_set_mask(PCIDevice *dev, int vector, bool mask, Error **errp)
32208cf3dc6SJagannathan Raman {
32308cf3dc6SJagannathan Raman     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
32408cf3dc6SJagannathan Raman     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
32508cf3dc6SJagannathan Raman     uint32_t irq_state, vector_mask, pending;
32608cf3dc6SJagannathan Raman 
32771e3d004SJagannathan Raman     if (vector >= PCI_MSI_VECTORS_MAX) {
32808cf3dc6SJagannathan Raman         error_setg(errp, "msi: vector %d not allocated. max vector is %d",
32971e3d004SJagannathan Raman                    vector, (PCI_MSI_VECTORS_MAX - 1));
33008cf3dc6SJagannathan Raman         return;
33108cf3dc6SJagannathan Raman     }
33208cf3dc6SJagannathan Raman 
33308cf3dc6SJagannathan Raman     vector_mask = (1U << vector);
33408cf3dc6SJagannathan Raman 
33508cf3dc6SJagannathan Raman     irq_state = pci_get_long(dev->config + msi_mask_off(dev, msi64bit));
33608cf3dc6SJagannathan Raman 
33708cf3dc6SJagannathan Raman     if (mask) {
33808cf3dc6SJagannathan Raman         irq_state |= vector_mask;
33908cf3dc6SJagannathan Raman     } else {
34008cf3dc6SJagannathan Raman         irq_state &= ~vector_mask;
34108cf3dc6SJagannathan Raman     }
34208cf3dc6SJagannathan Raman 
34308cf3dc6SJagannathan Raman     pci_set_long(dev->config + msi_mask_off(dev, msi64bit), irq_state);
34408cf3dc6SJagannathan Raman 
34508cf3dc6SJagannathan Raman     pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit));
34608cf3dc6SJagannathan Raman     if (!mask && (pending & vector_mask)) {
34708cf3dc6SJagannathan Raman         pending &= ~vector_mask;
34808cf3dc6SJagannathan Raman         pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending);
34908cf3dc6SJagannathan Raman         msi_notify(dev, vector);
35008cf3dc6SJagannathan Raman     }
35108cf3dc6SJagannathan Raman }
35208cf3dc6SJagannathan Raman 
msi_notify(PCIDevice * dev,unsigned int vector)353315a1350SMichael S. Tsirkin void msi_notify(PCIDevice *dev, unsigned int vector)
354315a1350SMichael S. Tsirkin {
355315a1350SMichael S. Tsirkin     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
356315a1350SMichael S. Tsirkin     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
357315a1350SMichael S. Tsirkin     unsigned int nr_vectors = msi_nr_vectors(flags);
358315a1350SMichael S. Tsirkin     MSIMessage msg;
359315a1350SMichael S. Tsirkin 
360315a1350SMichael S. Tsirkin     assert(vector < nr_vectors);
361315a1350SMichael S. Tsirkin     if (msi_is_masked(dev, vector)) {
362315a1350SMichael S. Tsirkin         assert(flags & PCI_MSI_FLAGS_MASKBIT);
363315a1350SMichael S. Tsirkin         pci_long_test_and_set_mask(
364315a1350SMichael S. Tsirkin             dev->config + msi_pending_off(dev, msi64bit), 1U << vector);
365315a1350SMichael S. Tsirkin         MSI_DEV_PRINTF(dev, "pending vector 0x%x\n", vector);
366315a1350SMichael S. Tsirkin         return;
367315a1350SMichael S. Tsirkin     }
368315a1350SMichael S. Tsirkin 
369315a1350SMichael S. Tsirkin     msg = msi_get_message(dev, vector);
370315a1350SMichael S. Tsirkin 
371315a1350SMichael S. Tsirkin     MSI_DEV_PRINTF(dev,
372315a1350SMichael S. Tsirkin                    "notify vector 0x%x"
373315a1350SMichael S. Tsirkin                    " address: 0x%"PRIx64" data: 0x%"PRIx32"\n",
374315a1350SMichael S. Tsirkin                    vector, msg.address, msg.data);
37538d40ff1SPavel Fedin     msi_send_message(dev, msg);
37638d40ff1SPavel Fedin }
37738d40ff1SPavel Fedin 
msi_send_message(PCIDevice * dev,MSIMessage msg)37838d40ff1SPavel Fedin void msi_send_message(PCIDevice *dev, MSIMessage msg)
37938d40ff1SPavel Fedin {
38008cf3dc6SJagannathan Raman     dev->msi_trigger(dev, msg);
381315a1350SMichael S. Tsirkin }
382315a1350SMichael S. Tsirkin 
383315a1350SMichael S. Tsirkin /* Normally called by pci_default_write_config(). */
msi_write_config(PCIDevice * dev,uint32_t addr,uint32_t val,int len)384315a1350SMichael S. Tsirkin void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len)
385315a1350SMichael S. Tsirkin {
386315a1350SMichael S. Tsirkin     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
387315a1350SMichael S. Tsirkin     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
388315a1350SMichael S. Tsirkin     bool msi_per_vector_mask = flags & PCI_MSI_FLAGS_MASKBIT;
389315a1350SMichael S. Tsirkin     unsigned int nr_vectors;
390315a1350SMichael S. Tsirkin     uint8_t log_num_vecs;
391315a1350SMichael S. Tsirkin     uint8_t log_max_vecs;
392315a1350SMichael S. Tsirkin     unsigned int vector;
393315a1350SMichael S. Tsirkin     uint32_t pending;
394315a1350SMichael S. Tsirkin 
395315a1350SMichael S. Tsirkin     if (!msi_present(dev) ||
396315a1350SMichael S. Tsirkin         !ranges_overlap(addr, len, dev->msi_cap, msi_cap_sizeof(flags))) {
397315a1350SMichael S. Tsirkin         return;
398315a1350SMichael S. Tsirkin     }
399315a1350SMichael S. Tsirkin 
400315a1350SMichael S. Tsirkin #ifdef MSI_DEBUG
401315a1350SMichael S. Tsirkin     MSI_DEV_PRINTF(dev, "addr 0x%"PRIx32" val 0x%"PRIx32" len %d\n",
402315a1350SMichael S. Tsirkin                    addr, val, len);
403315a1350SMichael S. Tsirkin     MSI_DEV_PRINTF(dev, "ctrl: 0x%"PRIx16" address: 0x%"PRIx32,
404315a1350SMichael S. Tsirkin                    flags,
405315a1350SMichael S. Tsirkin                    pci_get_long(dev->config + msi_address_lo_off(dev)));
406315a1350SMichael S. Tsirkin     if (msi64bit) {
407315a1350SMichael S. Tsirkin         fprintf(stderr, " address-hi: 0x%"PRIx32,
408315a1350SMichael S. Tsirkin                 pci_get_long(dev->config + msi_address_hi_off(dev)));
409315a1350SMichael S. Tsirkin     }
410315a1350SMichael S. Tsirkin     fprintf(stderr, " data: 0x%"PRIx16,
411315a1350SMichael S. Tsirkin             pci_get_word(dev->config + msi_data_off(dev, msi64bit)));
412315a1350SMichael S. Tsirkin     if (flags & PCI_MSI_FLAGS_MASKBIT) {
413315a1350SMichael S. Tsirkin         fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32,
414315a1350SMichael S. Tsirkin                 pci_get_long(dev->config + msi_mask_off(dev, msi64bit)),
415315a1350SMichael S. Tsirkin                 pci_get_long(dev->config + msi_pending_off(dev, msi64bit)));
416315a1350SMichael S. Tsirkin     }
417315a1350SMichael S. Tsirkin     fprintf(stderr, "\n");
418315a1350SMichael S. Tsirkin #endif
419315a1350SMichael S. Tsirkin 
4206096cf78SDavid Woodhouse     if (xen_mode == XEN_EMULATE) {
4216096cf78SDavid Woodhouse         for (vector = 0; vector < msi_nr_vectors(flags); vector++) {
4226096cf78SDavid Woodhouse             MSIMessage msg = msi_prepare_message(dev, vector);
4236096cf78SDavid Woodhouse 
4246096cf78SDavid Woodhouse             xen_evtchn_snoop_msi(dev, false, vector, msg.address, msg.data,
4256096cf78SDavid Woodhouse                                  msi_is_masked(dev, vector));
4266096cf78SDavid Woodhouse         }
4276096cf78SDavid Woodhouse     }
4286096cf78SDavid Woodhouse 
429315a1350SMichael S. Tsirkin     if (!(flags & PCI_MSI_FLAGS_ENABLE)) {
430315a1350SMichael S. Tsirkin         return;
431315a1350SMichael S. Tsirkin     }
432315a1350SMichael S. Tsirkin 
433315a1350SMichael S. Tsirkin     /*
434315a1350SMichael S. Tsirkin      * Now MSI is enabled, clear INTx# interrupts.
435315a1350SMichael S. Tsirkin      * the driver is prohibited from writing enable bit to mask
436315a1350SMichael S. Tsirkin      * a service request. But the guest OS could do this.
437315a1350SMichael S. Tsirkin      * So we just discard the interrupts as moderate fallback.
438315a1350SMichael S. Tsirkin      *
439315a1350SMichael S. Tsirkin      * 6.8.3.3. Enabling Operation
440315a1350SMichael S. Tsirkin      *   While enabled for MSI or MSI-X operation, a function is prohibited
441315a1350SMichael S. Tsirkin      *   from using its INTx# pin (if implemented) to request
442315a1350SMichael S. Tsirkin      *   service (MSI, MSI-X, and INTx# are mutually exclusive).
443315a1350SMichael S. Tsirkin      */
444315a1350SMichael S. Tsirkin     pci_device_deassert_intx(dev);
445315a1350SMichael S. Tsirkin 
446315a1350SMichael S. Tsirkin     /*
447315a1350SMichael S. Tsirkin      * nr_vectors might be set bigger than capable. So clamp it.
448315a1350SMichael S. Tsirkin      * This is not legal by spec, so we can do anything we like,
449315a1350SMichael S. Tsirkin      * just don't crash the host
450315a1350SMichael S. Tsirkin      */
451315a1350SMichael S. Tsirkin     log_num_vecs =
452786a4ea8SStefan Hajnoczi         (flags & PCI_MSI_FLAGS_QSIZE) >> ctz32(PCI_MSI_FLAGS_QSIZE);
453315a1350SMichael S. Tsirkin     log_max_vecs =
454786a4ea8SStefan Hajnoczi         (flags & PCI_MSI_FLAGS_QMASK) >> ctz32(PCI_MSI_FLAGS_QMASK);
455315a1350SMichael S. Tsirkin     if (log_num_vecs > log_max_vecs) {
456315a1350SMichael S. Tsirkin         flags &= ~PCI_MSI_FLAGS_QSIZE;
457786a4ea8SStefan Hajnoczi         flags |= log_max_vecs << ctz32(PCI_MSI_FLAGS_QSIZE);
458315a1350SMichael S. Tsirkin         pci_set_word(dev->config + msi_flags_off(dev), flags);
459315a1350SMichael S. Tsirkin     }
460315a1350SMichael S. Tsirkin 
461315a1350SMichael S. Tsirkin     if (!msi_per_vector_mask) {
462315a1350SMichael S. Tsirkin         /* if per vector masking isn't supported,
463315a1350SMichael S. Tsirkin            there is no pending interrupt. */
464315a1350SMichael S. Tsirkin         return;
465315a1350SMichael S. Tsirkin     }
466315a1350SMichael S. Tsirkin 
467315a1350SMichael S. Tsirkin     nr_vectors = msi_nr_vectors(flags);
468315a1350SMichael S. Tsirkin 
469315a1350SMichael S. Tsirkin     /* This will discard pending interrupts, if any. */
470315a1350SMichael S. Tsirkin     pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit));
471315a1350SMichael S. Tsirkin     pending &= 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors);
472315a1350SMichael S. Tsirkin     pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending);
473315a1350SMichael S. Tsirkin 
474315a1350SMichael S. Tsirkin     /* deliver pending interrupts which are unmasked */
475315a1350SMichael S. Tsirkin     for (vector = 0; vector < nr_vectors; ++vector) {
476315a1350SMichael S. Tsirkin         if (msi_is_masked(dev, vector) || !(pending & (1U << vector))) {
477315a1350SMichael S. Tsirkin             continue;
478315a1350SMichael S. Tsirkin         }
479315a1350SMichael S. Tsirkin 
480315a1350SMichael S. Tsirkin         pci_long_test_and_clear_mask(
481315a1350SMichael S. Tsirkin             dev->config + msi_pending_off(dev, msi64bit), 1U << vector);
482315a1350SMichael S. Tsirkin         msi_notify(dev, vector);
483315a1350SMichael S. Tsirkin     }
484315a1350SMichael S. Tsirkin }
485315a1350SMichael S. Tsirkin 
msi_nr_vectors_allocated(const PCIDevice * dev)486315a1350SMichael S. Tsirkin unsigned int msi_nr_vectors_allocated(const PCIDevice *dev)
487315a1350SMichael S. Tsirkin {
488315a1350SMichael S. Tsirkin     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
489315a1350SMichael S. Tsirkin     return msi_nr_vectors(flags);
490315a1350SMichael S. Tsirkin }
491