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