| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * msi.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp> | 
					
						
							|  |  |  |  *                    VA Linux Systems Japan K.K. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation; either version 2 of the License, or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License along | 
					
						
							|  |  |  |  * with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:17:15 +00:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2012-12-12 23:05:42 +02:00
										 |  |  | #include "hw/pci/msi.h"
 | 
					
						
							| 
									
										
										
										
											2016-01-13 14:59:09 +00:00
										 |  |  | #include "hw/xen/xen.h"
 | 
					
						
							| 
									
										
										
										
											2012-12-17 18:20:00 +01:00
										 |  |  | #include "qemu/range.h"
 | 
					
						
							| 
									
										
										
										
											2016-06-20 14:13:39 +08:00
										 |  |  | #include "qapi/error.h"
 | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-13 23:35:46 +00:00
										 |  |  | #include "hw/i386/kvm/xen_evtchn.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | /* PCI_MSI_ADDRESS_LO */ | 
					
						
							|  |  |  | #define PCI_MSI_ADDRESS_LO_MASK         (~0x3)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* If we get rid of cap allocator, we won't need those. */ | 
					
						
							|  |  |  | #define PCI_MSI_32_SIZEOF       0x0a
 | 
					
						
							|  |  |  | #define PCI_MSI_64_SIZEOF       0x0e
 | 
					
						
							|  |  |  | #define PCI_MSI_32M_SIZEOF      0x14
 | 
					
						
							|  |  |  | #define PCI_MSI_64M_SIZEOF      0x18
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define PCI_MSI_VECTORS_MAX     32
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-04 11:24:28 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Flag for interrupt controllers to declare broken MSI/MSI-X support. | 
					
						
							|  |  |  |  * values: false - broken; true - non-broken. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Setting this flag to false will remove MSI/MSI-X capability from all devices. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2016-03-23 15:59:57 +01:00
										 |  |  |  * It is preferable for controllers to set this to true (non-broken) even if | 
					
						
							| 
									
										
										
										
											2016-03-04 11:24:28 +02:00
										 |  |  |  * they do not actually support MSI/MSI-X: guests normally probe the controller | 
					
						
							|  |  |  |  * type and do not attempt to enable MSI/MSI-X with interrupt controllers not | 
					
						
							|  |  |  |  * supporting such, so removing the capability is not required, and | 
					
						
							|  |  |  |  * it seems cleaner to have a given device look the same for all boards. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * TODO: some existing controllers violate the above rule. Identify and fix them. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | bool msi_nonbroken; | 
					
						
							| 
									
										
										
										
											2011-10-15 14:33:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | /* If we get rid of cap allocator, we won't need this. */ | 
					
						
							|  |  |  | static inline uint8_t msi_cap_sizeof(uint16_t flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     switch (flags & (PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT)) { | 
					
						
							|  |  |  |     case PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT: | 
					
						
							|  |  |  |         return PCI_MSI_64M_SIZEOF; | 
					
						
							|  |  |  |     case PCI_MSI_FLAGS_64BIT: | 
					
						
							|  |  |  |         return PCI_MSI_64_SIZEOF; | 
					
						
							|  |  |  |     case PCI_MSI_FLAGS_MASKBIT: | 
					
						
							|  |  |  |         return PCI_MSI_32M_SIZEOF; | 
					
						
							|  |  |  |     case 0: | 
					
						
							|  |  |  |         return PCI_MSI_32_SIZEOF; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         abort(); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //#define MSI_DEBUG
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef MSI_DEBUG
 | 
					
						
							|  |  |  | # define MSI_DPRINTF(fmt, ...)                                          \
 | 
					
						
							|  |  |  |     fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | # define MSI_DPRINTF(fmt, ...)  do { } while (0)
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #define MSI_DEV_PRINTF(dev, fmt, ...)                                   \
 | 
					
						
							|  |  |  |     MSI_DPRINTF("%s:%x " fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline unsigned int msi_nr_vectors(uint16_t flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return 1U << | 
					
						
							| 
									
										
										
										
											2015-03-23 15:29:26 +00:00
										 |  |  |         ((flags & PCI_MSI_FLAGS_QSIZE) >> ctz32(PCI_MSI_FLAGS_QSIZE)); | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline uint8_t msi_flags_off(const PCIDevice* dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return dev->msi_cap + PCI_MSI_FLAGS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline uint8_t msi_address_lo_off(const PCIDevice* dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return dev->msi_cap + PCI_MSI_ADDRESS_LO; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline uint8_t msi_address_hi_off(const PCIDevice* dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return dev->msi_cap + PCI_MSI_ADDRESS_HI; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline uint8_t msi_data_off(const PCIDevice* dev, bool msi64bit) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return dev->msi_cap + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline uint8_t msi_mask_off(const PCIDevice* dev, bool msi64bit) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return dev->msi_cap + (msi64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline uint8_t msi_pending_off(const PCIDevice* dev, bool msi64bit) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return dev->msi_cap + (msi64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-19 10:35:07 +10:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Special API for POWER to configure the vectors through | 
					
						
							|  |  |  |  * a side channel. Should never be used by devices. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void msi_set_message(PCIDevice *dev, MSIMessage msg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); | 
					
						
							|  |  |  |     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (msi64bit) { | 
					
						
							|  |  |  |         pci_set_quad(dev->config + msi_address_lo_off(dev), msg.address); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         pci_set_long(dev->config + msi_address_lo_off(dev), msg.address); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     pci_set_word(dev->config + msi_data_off(dev, msi64bit), msg.data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-13 16:26:33 -04:00
										 |  |  | static MSIMessage msi_prepare_message(PCIDevice *dev, unsigned int vector) | 
					
						
							| 
									
										
										
										
											2012-10-02 13:22:07 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); | 
					
						
							|  |  |  |     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; | 
					
						
							|  |  |  |     unsigned int nr_vectors = msi_nr_vectors(flags); | 
					
						
							|  |  |  |     MSIMessage msg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(vector < nr_vectors); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (msi64bit) { | 
					
						
							|  |  |  |         msg.address = pci_get_quad(dev->config + msi_address_lo_off(dev)); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         msg.address = pci_get_long(dev->config + msi_address_lo_off(dev)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* upper bit 31:16 is zero */ | 
					
						
							|  |  |  |     msg.data = pci_get_word(dev->config + msi_data_off(dev, msi64bit)); | 
					
						
							|  |  |  |     if (nr_vectors > 1) { | 
					
						
							|  |  |  |         msg.data &= ~(nr_vectors - 1); | 
					
						
							|  |  |  |         msg.data |= vector; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return msg; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-13 16:26:33 -04:00
										 |  |  | MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return dev->msi_prepare_message(dev, vector); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | bool msi_enabled(const PCIDevice *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return msi_present(dev) && | 
					
						
							|  |  |  |         (pci_get_word(dev->config + msi_flags_off(dev)) & | 
					
						
							|  |  |  |          PCI_MSI_FLAGS_ENABLE); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-10 17:54:23 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Make PCI device @dev MSI-capable. | 
					
						
							|  |  |  |  * Non-zero @offset puts capability MSI at that offset in PCI config | 
					
						
							|  |  |  |  * space. | 
					
						
							|  |  |  |  * @nr_vectors is the number of MSI vectors (1, 2, 4, 8, 16 or 32). | 
					
						
							|  |  |  |  * If @msi64bit, make the device capable of sending a 64-bit message | 
					
						
							|  |  |  |  * address. | 
					
						
							|  |  |  |  * If @msi_per_vector_mask, make the device support per-vector masking. | 
					
						
							| 
									
										
										
										
											2016-06-20 14:13:39 +08:00
										 |  |  |  * @errp is for returning errors. | 
					
						
							|  |  |  |  * Return 0 on success; set @errp and return -errno on error. | 
					
						
							| 
									
										
										
										
											2016-06-10 17:54:23 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * -ENOTSUP means lacking msi support for a msi-capable platform. | 
					
						
							|  |  |  |  * -EINVAL means capability overlap, happens when @offset is non-zero, | 
					
						
							|  |  |  |  *  also means a programming error, except device assignment, which can check | 
					
						
							|  |  |  |  *  if a real HW is broken. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | int msi_init(struct PCIDevice *dev, uint8_t offset, | 
					
						
							| 
									
										
										
										
											2016-06-20 14:13:39 +08:00
										 |  |  |              unsigned int nr_vectors, bool msi64bit, | 
					
						
							|  |  |  |              bool msi_per_vector_mask, Error **errp) | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | { | 
					
						
							|  |  |  |     unsigned int vectors_order; | 
					
						
							|  |  |  |     uint16_t flags; | 
					
						
							|  |  |  |     uint8_t cap_size; | 
					
						
							|  |  |  |     int config_offset; | 
					
						
							| 
									
										
										
										
											2011-10-15 14:33:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-04 11:24:28 +02:00
										 |  |  |     if (!msi_nonbroken) { | 
					
						
							| 
									
										
										
										
											2016-06-20 14:13:39 +08:00
										 |  |  |         error_setg(errp, "MSI is not supported by interrupt controller"); | 
					
						
							| 
									
										
										
										
											2011-10-15 14:33:17 +02:00
										 |  |  |         return -ENOTSUP; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     MSI_DEV_PRINTF(dev, | 
					
						
							|  |  |  |                    "init offset: 0x%"PRIx8" vector: %"PRId8 | 
					
						
							|  |  |  |                    " 64bit %d mask %d\n", | 
					
						
							|  |  |  |                    offset, nr_vectors, msi64bit, msi_per_vector_mask); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(!(nr_vectors & (nr_vectors - 1)));   /* power of 2 */ | 
					
						
							|  |  |  |     assert(nr_vectors > 0); | 
					
						
							|  |  |  |     assert(nr_vectors <= PCI_MSI_VECTORS_MAX); | 
					
						
							|  |  |  |     /* the nr of MSI vectors is up to 32 */ | 
					
						
							| 
									
										
										
										
											2015-03-23 15:29:26 +00:00
										 |  |  |     vectors_order = ctz32(nr_vectors); | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-23 15:29:26 +00:00
										 |  |  |     flags = vectors_order << ctz32(PCI_MSI_FLAGS_QMASK); | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     if (msi64bit) { | 
					
						
							|  |  |  |         flags |= PCI_MSI_FLAGS_64BIT; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (msi_per_vector_mask) { | 
					
						
							|  |  |  |         flags |= PCI_MSI_FLAGS_MASKBIT; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cap_size = msi_cap_sizeof(flags); | 
					
						
							| 
									
										
										
										
											2017-06-27 14:16:51 +08:00
										 |  |  |     config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, | 
					
						
							| 
									
										
										
										
											2016-06-20 14:13:39 +08:00
										 |  |  |                                         cap_size, errp); | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     if (config_offset < 0) { | 
					
						
							|  |  |  |         return config_offset; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dev->msi_cap = config_offset; | 
					
						
							|  |  |  |     dev->cap_present |= QEMU_PCI_CAP_MSI; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pci_set_word(dev->config + msi_flags_off(dev), flags); | 
					
						
							|  |  |  |     pci_set_word(dev->wmask + msi_flags_off(dev), | 
					
						
							|  |  |  |                  PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); | 
					
						
							|  |  |  |     pci_set_long(dev->wmask + msi_address_lo_off(dev), | 
					
						
							|  |  |  |                  PCI_MSI_ADDRESS_LO_MASK); | 
					
						
							|  |  |  |     if (msi64bit) { | 
					
						
							|  |  |  |         pci_set_long(dev->wmask + msi_address_hi_off(dev), 0xffffffff); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     pci_set_word(dev->wmask + msi_data_off(dev, msi64bit), 0xffff); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (msi_per_vector_mask) { | 
					
						
							| 
									
										
										
										
											2011-04-26 10:29:36 +02:00
										 |  |  |         /* Make mask bits 0 to nr_vectors - 1 writable. */ | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |         pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit), | 
					
						
							|  |  |  |                      0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-10 17:54:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-13 16:26:33 -04:00
										 |  |  |     dev->msi_prepare_message = msi_prepare_message; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-10 17:54:32 +08:00
										 |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void msi_uninit(struct PCIDevice *dev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-05-02 20:00:47 +02:00
										 |  |  |     uint16_t flags; | 
					
						
							|  |  |  |     uint8_t cap_size; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-04 16:53:48 +02:00
										 |  |  |     if (!msi_present(dev)) { | 
					
						
							| 
									
										
										
										
											2011-05-02 20:00:47 +02:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     flags = pci_get_word(dev->config + msi_flags_off(dev)); | 
					
						
							|  |  |  |     cap_size = msi_cap_sizeof(flags); | 
					
						
							| 
									
										
										
										
											2011-06-07 19:26:20 +02:00
										 |  |  |     pci_del_capability(dev, PCI_CAP_ID_MSI, cap_size); | 
					
						
							| 
									
										
										
										
											2011-05-02 20:00:47 +02:00
										 |  |  |     dev->cap_present &= ~QEMU_PCI_CAP_MSI; | 
					
						
							| 
									
										
										
										
											2022-06-13 16:26:33 -04:00
										 |  |  |     dev->msi_prepare_message = NULL; | 
					
						
							| 
									
										
										
										
											2011-05-02 20:00:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     MSI_DEV_PRINTF(dev, "uninit\n"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void msi_reset(PCIDevice *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint16_t flags; | 
					
						
							|  |  |  |     bool msi64bit; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-11 11:42:37 -03:00
										 |  |  |     if (!msi_present(dev)) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     flags = pci_get_word(dev->config + msi_flags_off(dev)); | 
					
						
							|  |  |  |     flags &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); | 
					
						
							|  |  |  |     msi64bit = flags & PCI_MSI_FLAGS_64BIT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pci_set_word(dev->config + msi_flags_off(dev), flags); | 
					
						
							|  |  |  |     pci_set_long(dev->config + msi_address_lo_off(dev), 0); | 
					
						
							|  |  |  |     if (msi64bit) { | 
					
						
							|  |  |  |         pci_set_long(dev->config + msi_address_hi_off(dev), 0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     pci_set_word(dev->config + msi_data_off(dev, msi64bit), 0); | 
					
						
							|  |  |  |     if (flags & PCI_MSI_FLAGS_MASKBIT) { | 
					
						
							|  |  |  |         pci_set_long(dev->config + msi_mask_off(dev, msi64bit), 0); | 
					
						
							|  |  |  |         pci_set_long(dev->config + msi_pending_off(dev, msi64bit), 0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     MSI_DEV_PRINTF(dev, "reset\n"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-16 11:08:14 +08:00
										 |  |  | bool msi_is_masked(const PCIDevice *dev, unsigned int vector) | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | { | 
					
						
							|  |  |  |     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); | 
					
						
							| 
									
										
										
										
											2016-01-13 14:59:09 +00:00
										 |  |  |     uint32_t mask, data; | 
					
						
							|  |  |  |     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     assert(vector < PCI_MSI_VECTORS_MAX); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-13 14:59:09 +00:00
										 |  |  |     data = pci_get_word(dev->config + msi_data_off(dev, msi64bit)); | 
					
						
							|  |  |  |     if (xen_is_pirq_msi(data)) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     mask = pci_get_long(dev->config + | 
					
						
							|  |  |  |                         msi_mask_off(dev, flags & PCI_MSI_FLAGS_64BIT)); | 
					
						
							|  |  |  |     return mask & (1U << vector); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-13 16:26:33 -04:00
										 |  |  | void msi_set_mask(PCIDevice *dev, int vector, bool mask, Error **errp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); | 
					
						
							|  |  |  |     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; | 
					
						
							|  |  |  |     uint32_t irq_state, vector_mask, pending; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-23 11:38:44 -04:00
										 |  |  |     if (vector >= PCI_MSI_VECTORS_MAX) { | 
					
						
							| 
									
										
										
										
											2022-06-13 16:26:33 -04:00
										 |  |  |         error_setg(errp, "msi: vector %d not allocated. max vector is %d", | 
					
						
							| 
									
										
										
										
											2022-06-23 11:38:44 -04:00
										 |  |  |                    vector, (PCI_MSI_VECTORS_MAX - 1)); | 
					
						
							| 
									
										
										
										
											2022-06-13 16:26:33 -04:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     vector_mask = (1U << vector); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     irq_state = pci_get_long(dev->config + msi_mask_off(dev, msi64bit)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (mask) { | 
					
						
							|  |  |  |         irq_state |= vector_mask; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         irq_state &= ~vector_mask; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pci_set_long(dev->config + msi_mask_off(dev, msi64bit), irq_state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit)); | 
					
						
							|  |  |  |     if (!mask && (pending & vector_mask)) { | 
					
						
							|  |  |  |         pending &= ~vector_mask; | 
					
						
							|  |  |  |         pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending); | 
					
						
							|  |  |  |         msi_notify(dev, vector); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | void msi_notify(PCIDevice *dev, unsigned int vector) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); | 
					
						
							|  |  |  |     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; | 
					
						
							|  |  |  |     unsigned int nr_vectors = msi_nr_vectors(flags); | 
					
						
							| 
									
										
										
										
											2012-10-02 13:22:07 -06:00
										 |  |  |     MSIMessage msg; | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     assert(vector < nr_vectors); | 
					
						
							|  |  |  |     if (msi_is_masked(dev, vector)) { | 
					
						
							|  |  |  |         assert(flags & PCI_MSI_FLAGS_MASKBIT); | 
					
						
							|  |  |  |         pci_long_test_and_set_mask( | 
					
						
							|  |  |  |             dev->config + msi_pending_off(dev, msi64bit), 1U << vector); | 
					
						
							|  |  |  |         MSI_DEV_PRINTF(dev, "pending vector 0x%x\n", vector); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-02 13:22:07 -06:00
										 |  |  |     msg = msi_get_message(dev, vector); | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     MSI_DEV_PRINTF(dev, | 
					
						
							|  |  |  |                    "notify vector 0x%x" | 
					
						
							|  |  |  |                    " address: 0x%"PRIx64" data: 0x%"PRIx32"\n", | 
					
						
							| 
									
										
										
										
											2012-10-02 13:22:07 -06:00
										 |  |  |                    vector, msg.address, msg.data); | 
					
						
							| 
									
										
										
										
											2015-05-27 15:59:59 +03:00
										 |  |  |     msi_send_message(dev, msg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void msi_send_message(PCIDevice *dev, MSIMessage msg) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-06-13 16:26:33 -04:00
										 |  |  |     dev->msi_trigger(dev, msg); | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-11 11:42:40 -03:00
										 |  |  | /* Normally called by pci_default_write_config(). */ | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); | 
					
						
							|  |  |  |     bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; | 
					
						
							|  |  |  |     bool msi_per_vector_mask = flags & PCI_MSI_FLAGS_MASKBIT; | 
					
						
							|  |  |  |     unsigned int nr_vectors; | 
					
						
							|  |  |  |     uint8_t log_num_vecs; | 
					
						
							|  |  |  |     uint8_t log_max_vecs; | 
					
						
							|  |  |  |     unsigned int vector; | 
					
						
							|  |  |  |     uint32_t pending; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-11 11:42:39 -03:00
										 |  |  |     if (!msi_present(dev) || | 
					
						
							|  |  |  |         !ranges_overlap(addr, len, dev->msi_cap, msi_cap_sizeof(flags))) { | 
					
						
							| 
									
										
										
										
											2010-10-27 16:14:56 +02:00
										 |  |  |         return; | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-27 16:14:56 +02:00
										 |  |  | #ifdef MSI_DEBUG
 | 
					
						
							|  |  |  |     MSI_DEV_PRINTF(dev, "addr 0x%"PRIx32" val 0x%"PRIx32" len %d\n", | 
					
						
							|  |  |  |                    addr, val, len); | 
					
						
							|  |  |  |     MSI_DEV_PRINTF(dev, "ctrl: 0x%"PRIx16" address: 0x%"PRIx32, | 
					
						
							|  |  |  |                    flags, | 
					
						
							|  |  |  |                    pci_get_long(dev->config + msi_address_lo_off(dev))); | 
					
						
							|  |  |  |     if (msi64bit) { | 
					
						
							| 
									
										
										
										
											2010-10-27 16:28:22 +02:00
										 |  |  |         fprintf(stderr, " address-hi: 0x%"PRIx32, | 
					
						
							| 
									
										
										
										
											2010-10-27 16:14:56 +02:00
										 |  |  |                 pci_get_long(dev->config + msi_address_hi_off(dev))); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     fprintf(stderr, " data: 0x%"PRIx16, | 
					
						
							|  |  |  |             pci_get_word(dev->config + msi_data_off(dev, msi64bit))); | 
					
						
							|  |  |  |     if (flags & PCI_MSI_FLAGS_MASKBIT) { | 
					
						
							|  |  |  |         fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32, | 
					
						
							|  |  |  |                 pci_get_long(dev->config + msi_mask_off(dev, msi64bit)), | 
					
						
							|  |  |  |                 pci_get_long(dev->config + msi_pending_off(dev, msi64bit))); | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-10-27 16:14:56 +02:00
										 |  |  |     fprintf(stderr, "\n"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-13 23:35:46 +00:00
										 |  |  |     if (xen_mode == XEN_EMULATE) { | 
					
						
							|  |  |  |         for (vector = 0; vector < msi_nr_vectors(flags); vector++) { | 
					
						
							|  |  |  |             MSIMessage msg = msi_prepare_message(dev, vector); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             xen_evtchn_snoop_msi(dev, false, vector, msg.address, msg.data, | 
					
						
							|  |  |  |                                  msi_is_masked(dev, vector)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     if (!(flags & PCI_MSI_FLAGS_ENABLE)) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /*
 | 
					
						
							|  |  |  |      * Now MSI is enabled, clear INTx# interrupts. | 
					
						
							|  |  |  |      * the driver is prohibited from writing enable bit to mask | 
					
						
							|  |  |  |      * a service request. But the guest OS could do this. | 
					
						
							|  |  |  |      * So we just discard the interrupts as moderate fallback. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * 6.8.3.3. Enabling Operation | 
					
						
							|  |  |  |      *   While enabled for MSI or MSI-X operation, a function is prohibited | 
					
						
							|  |  |  |      *   from using its INTx# pin (if implemented) to request | 
					
						
							|  |  |  |      *   service (MSI, MSI-X, and INTx# are mutually exclusive). | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2011-01-20 16:21:39 +09:00
										 |  |  |     pci_device_deassert_intx(dev); | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /*
 | 
					
						
							|  |  |  |      * nr_vectors might be set bigger than capable. So clamp it. | 
					
						
							|  |  |  |      * This is not legal by spec, so we can do anything we like, | 
					
						
							|  |  |  |      * just don't crash the host | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     log_num_vecs = | 
					
						
							| 
									
										
										
										
											2015-03-23 15:29:26 +00:00
										 |  |  |         (flags & PCI_MSI_FLAGS_QSIZE) >> ctz32(PCI_MSI_FLAGS_QSIZE); | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     log_max_vecs = | 
					
						
							| 
									
										
										
										
											2015-03-23 15:29:26 +00:00
										 |  |  |         (flags & PCI_MSI_FLAGS_QMASK) >> ctz32(PCI_MSI_FLAGS_QMASK); | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |     if (log_num_vecs > log_max_vecs) { | 
					
						
							|  |  |  |         flags &= ~PCI_MSI_FLAGS_QSIZE; | 
					
						
							| 
									
										
										
										
											2015-03-23 15:29:26 +00:00
										 |  |  |         flags |= log_max_vecs << ctz32(PCI_MSI_FLAGS_QSIZE); | 
					
						
							| 
									
										
										
										
											2010-10-19 18:06:32 +09:00
										 |  |  |         pci_set_word(dev->config + msi_flags_off(dev), flags); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!msi_per_vector_mask) { | 
					
						
							|  |  |  |         /* if per vector masking isn't supported,
 | 
					
						
							|  |  |  |            there is no pending interrupt. */ | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     nr_vectors = msi_nr_vectors(flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* This will discard pending interrupts, if any. */ | 
					
						
							|  |  |  |     pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit)); | 
					
						
							|  |  |  |     pending &= 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors); | 
					
						
							|  |  |  |     pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* deliver pending interrupts which are unmasked */ | 
					
						
							|  |  |  |     for (vector = 0; vector < nr_vectors; ++vector) { | 
					
						
							|  |  |  |         if (msi_is_masked(dev, vector) || !(pending & (1U << vector))) { | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         pci_long_test_and_clear_mask( | 
					
						
							|  |  |  |             dev->config + msi_pending_off(dev, msi64bit), 1U << vector); | 
					
						
							|  |  |  |         msi_notify(dev, vector); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | unsigned int msi_nr_vectors_allocated(const PCIDevice *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); | 
					
						
							|  |  |  |     return msi_nr_vectors(flags); | 
					
						
							|  |  |  | } |