Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 1 | /* |
| 2 | * msi.c |
| 3 | * |
| 4 | * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp> |
| 5 | * VA Linux Systems Japan K.K. |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; either version 2 of the License, or |
| 10 | * (at your option) any later version. |
| 11 | |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | |
| 17 | * You should have received a copy of the GNU General Public License along |
| 18 | * with this program; if not, see <http://www.gnu.org/licenses/>. |
| 19 | */ |
| 20 | |
| 21 | #include "msi.h" |
Blue Swirl | 5afb986 | 2010-09-18 05:53:14 +0000 | [diff] [blame] | 22 | #include "range.h" |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 23 | |
| 24 | /* Eventually those constants should go to Linux pci_regs.h */ |
| 25 | #define PCI_MSI_PENDING_32 0x10 |
| 26 | #define PCI_MSI_PENDING_64 0x14 |
| 27 | |
| 28 | /* PCI_MSI_ADDRESS_LO */ |
| 29 | #define PCI_MSI_ADDRESS_LO_MASK (~0x3) |
| 30 | |
| 31 | /* If we get rid of cap allocator, we won't need those. */ |
| 32 | #define PCI_MSI_32_SIZEOF 0x0a |
| 33 | #define PCI_MSI_64_SIZEOF 0x0e |
| 34 | #define PCI_MSI_32M_SIZEOF 0x14 |
| 35 | #define PCI_MSI_64M_SIZEOF 0x18 |
| 36 | |
| 37 | #define PCI_MSI_VECTORS_MAX 32 |
| 38 | |
Jan Kiszka | 60ba3cc | 2011-10-15 14:33:17 +0200 | [diff] [blame] | 39 | /* Flag for interrupt controller to declare MSI/MSI-X support */ |
| 40 | bool msi_supported; |
| 41 | |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 42 | /* If we get rid of cap allocator, we won't need this. */ |
| 43 | static inline uint8_t msi_cap_sizeof(uint16_t flags) |
| 44 | { |
| 45 | switch (flags & (PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT)) { |
| 46 | case PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT: |
| 47 | return PCI_MSI_64M_SIZEOF; |
| 48 | case PCI_MSI_FLAGS_64BIT: |
| 49 | return PCI_MSI_64_SIZEOF; |
| 50 | case PCI_MSI_FLAGS_MASKBIT: |
| 51 | return PCI_MSI_32M_SIZEOF; |
| 52 | case 0: |
| 53 | return PCI_MSI_32_SIZEOF; |
| 54 | default: |
| 55 | abort(); |
| 56 | break; |
| 57 | } |
| 58 | return 0; |
| 59 | } |
| 60 | |
| 61 | //#define MSI_DEBUG |
| 62 | |
| 63 | #ifdef MSI_DEBUG |
| 64 | # define MSI_DPRINTF(fmt, ...) \ |
| 65 | fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) |
| 66 | #else |
| 67 | # define MSI_DPRINTF(fmt, ...) do { } while (0) |
| 68 | #endif |
| 69 | #define MSI_DEV_PRINTF(dev, fmt, ...) \ |
| 70 | MSI_DPRINTF("%s:%x " fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) |
| 71 | |
| 72 | static inline unsigned int msi_nr_vectors(uint16_t flags) |
| 73 | { |
| 74 | return 1U << |
| 75 | ((flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1)); |
| 76 | } |
| 77 | |
| 78 | static inline uint8_t msi_flags_off(const PCIDevice* dev) |
| 79 | { |
| 80 | return dev->msi_cap + PCI_MSI_FLAGS; |
| 81 | } |
| 82 | |
| 83 | static inline uint8_t msi_address_lo_off(const PCIDevice* dev) |
| 84 | { |
| 85 | return dev->msi_cap + PCI_MSI_ADDRESS_LO; |
| 86 | } |
| 87 | |
| 88 | static inline uint8_t msi_address_hi_off(const PCIDevice* dev) |
| 89 | { |
| 90 | return dev->msi_cap + PCI_MSI_ADDRESS_HI; |
| 91 | } |
| 92 | |
| 93 | static inline uint8_t msi_data_off(const PCIDevice* dev, bool msi64bit) |
| 94 | { |
| 95 | return dev->msi_cap + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32); |
| 96 | } |
| 97 | |
| 98 | static inline uint8_t msi_mask_off(const PCIDevice* dev, bool msi64bit) |
| 99 | { |
| 100 | return dev->msi_cap + (msi64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32); |
| 101 | } |
| 102 | |
| 103 | static inline uint8_t msi_pending_off(const PCIDevice* dev, bool msi64bit) |
| 104 | { |
| 105 | return dev->msi_cap + (msi64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32); |
| 106 | } |
| 107 | |
Alexey Kardashevskiy | 932d4a4 | 2012-07-19 10:35:07 +1000 | [diff] [blame] | 108 | /* |
| 109 | * Special API for POWER to configure the vectors through |
| 110 | * a side channel. Should never be used by devices. |
| 111 | */ |
| 112 | void msi_set_message(PCIDevice *dev, MSIMessage msg) |
| 113 | { |
| 114 | uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); |
| 115 | bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; |
| 116 | |
| 117 | if (msi64bit) { |
| 118 | pci_set_quad(dev->config + msi_address_lo_off(dev), msg.address); |
| 119 | } else { |
| 120 | pci_set_long(dev->config + msi_address_lo_off(dev), msg.address); |
| 121 | } |
| 122 | pci_set_word(dev->config + msi_data_off(dev, msi64bit), msg.data); |
| 123 | } |
| 124 | |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 125 | bool msi_enabled(const PCIDevice *dev) |
| 126 | { |
| 127 | return msi_present(dev) && |
| 128 | (pci_get_word(dev->config + msi_flags_off(dev)) & |
| 129 | PCI_MSI_FLAGS_ENABLE); |
| 130 | } |
| 131 | |
| 132 | int msi_init(struct PCIDevice *dev, uint8_t offset, |
| 133 | unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask) |
| 134 | { |
| 135 | unsigned int vectors_order; |
| 136 | uint16_t flags; |
| 137 | uint8_t cap_size; |
| 138 | int config_offset; |
Jan Kiszka | 60ba3cc | 2011-10-15 14:33:17 +0200 | [diff] [blame] | 139 | |
| 140 | if (!msi_supported) { |
| 141 | return -ENOTSUP; |
| 142 | } |
| 143 | |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 144 | MSI_DEV_PRINTF(dev, |
| 145 | "init offset: 0x%"PRIx8" vector: %"PRId8 |
| 146 | " 64bit %d mask %d\n", |
| 147 | offset, nr_vectors, msi64bit, msi_per_vector_mask); |
| 148 | |
| 149 | assert(!(nr_vectors & (nr_vectors - 1))); /* power of 2 */ |
| 150 | assert(nr_vectors > 0); |
| 151 | assert(nr_vectors <= PCI_MSI_VECTORS_MAX); |
| 152 | /* the nr of MSI vectors is up to 32 */ |
| 153 | vectors_order = ffs(nr_vectors) - 1; |
| 154 | |
| 155 | flags = vectors_order << (ffs(PCI_MSI_FLAGS_QMASK) - 1); |
| 156 | if (msi64bit) { |
| 157 | flags |= PCI_MSI_FLAGS_64BIT; |
| 158 | } |
| 159 | if (msi_per_vector_mask) { |
| 160 | flags |= PCI_MSI_FLAGS_MASKBIT; |
| 161 | } |
| 162 | |
| 163 | cap_size = msi_cap_sizeof(flags); |
| 164 | config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size); |
| 165 | if (config_offset < 0) { |
| 166 | return config_offset; |
| 167 | } |
| 168 | |
| 169 | dev->msi_cap = config_offset; |
| 170 | dev->cap_present |= QEMU_PCI_CAP_MSI; |
| 171 | |
| 172 | pci_set_word(dev->config + msi_flags_off(dev), flags); |
| 173 | pci_set_word(dev->wmask + msi_flags_off(dev), |
| 174 | PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); |
| 175 | pci_set_long(dev->wmask + msi_address_lo_off(dev), |
| 176 | PCI_MSI_ADDRESS_LO_MASK); |
| 177 | if (msi64bit) { |
| 178 | pci_set_long(dev->wmask + msi_address_hi_off(dev), 0xffffffff); |
| 179 | } |
| 180 | pci_set_word(dev->wmask + msi_data_off(dev, msi64bit), 0xffff); |
| 181 | |
| 182 | if (msi_per_vector_mask) { |
Stefan Weil | ebabb67 | 2011-04-26 10:29:36 +0200 | [diff] [blame] | 183 | /* Make mask bits 0 to nr_vectors - 1 writable. */ |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 184 | pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit), |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 185 | 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors)); |
| 186 | } |
| 187 | return config_offset; |
| 188 | } |
| 189 | |
| 190 | void msi_uninit(struct PCIDevice *dev) |
| 191 | { |
Jan Kiszka | 45fe15c | 2011-05-02 20:00:47 +0200 | [diff] [blame] | 192 | uint16_t flags; |
| 193 | uint8_t cap_size; |
| 194 | |
Jan Kiszka | 44701ab | 2012-06-04 16:53:48 +0200 | [diff] [blame] | 195 | if (!msi_present(dev)) { |
Jan Kiszka | 45fe15c | 2011-05-02 20:00:47 +0200 | [diff] [blame] | 196 | return; |
| 197 | } |
| 198 | flags = pci_get_word(dev->config + msi_flags_off(dev)); |
| 199 | cap_size = msi_cap_sizeof(flags); |
Jan Kiszka | 4dad7f1 | 2011-06-07 19:26:20 +0200 | [diff] [blame] | 200 | pci_del_capability(dev, PCI_CAP_ID_MSI, cap_size); |
Jan Kiszka | 45fe15c | 2011-05-02 20:00:47 +0200 | [diff] [blame] | 201 | dev->cap_present &= ~QEMU_PCI_CAP_MSI; |
| 202 | |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 203 | MSI_DEV_PRINTF(dev, "uninit\n"); |
| 204 | } |
| 205 | |
| 206 | void msi_reset(PCIDevice *dev) |
| 207 | { |
| 208 | uint16_t flags; |
| 209 | bool msi64bit; |
| 210 | |
Jan Kiszka | 520064c | 2012-05-11 11:42:37 -0300 | [diff] [blame] | 211 | if (!msi_present(dev)) { |
| 212 | return; |
| 213 | } |
| 214 | |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 215 | flags = pci_get_word(dev->config + msi_flags_off(dev)); |
| 216 | flags &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); |
| 217 | msi64bit = flags & PCI_MSI_FLAGS_64BIT; |
| 218 | |
| 219 | pci_set_word(dev->config + msi_flags_off(dev), flags); |
| 220 | pci_set_long(dev->config + msi_address_lo_off(dev), 0); |
| 221 | if (msi64bit) { |
| 222 | pci_set_long(dev->config + msi_address_hi_off(dev), 0); |
| 223 | } |
| 224 | pci_set_word(dev->config + msi_data_off(dev, msi64bit), 0); |
| 225 | if (flags & PCI_MSI_FLAGS_MASKBIT) { |
| 226 | pci_set_long(dev->config + msi_mask_off(dev, msi64bit), 0); |
| 227 | pci_set_long(dev->config + msi_pending_off(dev, msi64bit), 0); |
| 228 | } |
| 229 | MSI_DEV_PRINTF(dev, "reset\n"); |
| 230 | } |
| 231 | |
| 232 | static bool msi_is_masked(const PCIDevice *dev, unsigned int vector) |
| 233 | { |
| 234 | uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); |
| 235 | uint32_t mask; |
| 236 | assert(vector < PCI_MSI_VECTORS_MAX); |
| 237 | |
| 238 | if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { |
| 239 | return false; |
| 240 | } |
| 241 | |
| 242 | mask = pci_get_long(dev->config + |
| 243 | msi_mask_off(dev, flags & PCI_MSI_FLAGS_64BIT)); |
| 244 | return mask & (1U << vector); |
| 245 | } |
| 246 | |
| 247 | void msi_notify(PCIDevice *dev, unsigned int vector) |
| 248 | { |
| 249 | uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); |
| 250 | bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; |
| 251 | unsigned int nr_vectors = msi_nr_vectors(flags); |
| 252 | uint64_t address; |
| 253 | uint32_t data; |
| 254 | |
| 255 | assert(vector < nr_vectors); |
| 256 | if (msi_is_masked(dev, vector)) { |
| 257 | assert(flags & PCI_MSI_FLAGS_MASKBIT); |
| 258 | pci_long_test_and_set_mask( |
| 259 | dev->config + msi_pending_off(dev, msi64bit), 1U << vector); |
| 260 | MSI_DEV_PRINTF(dev, "pending vector 0x%x\n", vector); |
| 261 | return; |
| 262 | } |
| 263 | |
Michael S. Tsirkin | b794ec7 | 2010-10-27 16:28:22 +0200 | [diff] [blame] | 264 | if (msi64bit) { |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 265 | address = pci_get_quad(dev->config + msi_address_lo_off(dev)); |
| 266 | } else { |
| 267 | address = pci_get_long(dev->config + msi_address_lo_off(dev)); |
| 268 | } |
| 269 | |
| 270 | /* upper bit 31:16 is zero */ |
| 271 | data = pci_get_word(dev->config + msi_data_off(dev, msi64bit)); |
| 272 | if (nr_vectors > 1) { |
| 273 | data &= ~(nr_vectors - 1); |
| 274 | data |= vector; |
| 275 | } |
| 276 | |
| 277 | MSI_DEV_PRINTF(dev, |
| 278 | "notify vector 0x%x" |
| 279 | " address: 0x%"PRIx64" data: 0x%"PRIx32"\n", |
| 280 | vector, address, data); |
Alexander Graf | c5d29d2 | 2011-07-05 18:28:05 +0200 | [diff] [blame] | 281 | stl_le_phys(address, data); |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 282 | } |
| 283 | |
Jan Kiszka | 95d6580 | 2012-05-11 11:42:40 -0300 | [diff] [blame] | 284 | /* Normally called by pci_default_write_config(). */ |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 285 | void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) |
| 286 | { |
| 287 | uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); |
| 288 | bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; |
| 289 | bool msi_per_vector_mask = flags & PCI_MSI_FLAGS_MASKBIT; |
| 290 | unsigned int nr_vectors; |
| 291 | uint8_t log_num_vecs; |
| 292 | uint8_t log_max_vecs; |
| 293 | unsigned int vector; |
| 294 | uint32_t pending; |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 295 | |
Jan Kiszka | 7c9958b | 2012-05-11 11:42:39 -0300 | [diff] [blame] | 296 | if (!msi_present(dev) || |
| 297 | !ranges_overlap(addr, len, dev->msi_cap, msi_cap_sizeof(flags))) { |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 298 | return; |
| 299 | } |
| 300 | |
Michael S. Tsirkin | 531a0b8 | 2010-10-27 16:14:56 +0200 | [diff] [blame] | 301 | #ifdef MSI_DEBUG |
| 302 | MSI_DEV_PRINTF(dev, "addr 0x%"PRIx32" val 0x%"PRIx32" len %d\n", |
| 303 | addr, val, len); |
| 304 | MSI_DEV_PRINTF(dev, "ctrl: 0x%"PRIx16" address: 0x%"PRIx32, |
| 305 | flags, |
| 306 | pci_get_long(dev->config + msi_address_lo_off(dev))); |
| 307 | if (msi64bit) { |
Michael S. Tsirkin | b794ec7 | 2010-10-27 16:28:22 +0200 | [diff] [blame] | 308 | fprintf(stderr, " address-hi: 0x%"PRIx32, |
Michael S. Tsirkin | 531a0b8 | 2010-10-27 16:14:56 +0200 | [diff] [blame] | 309 | pci_get_long(dev->config + msi_address_hi_off(dev))); |
| 310 | } |
| 311 | fprintf(stderr, " data: 0x%"PRIx16, |
| 312 | pci_get_word(dev->config + msi_data_off(dev, msi64bit))); |
| 313 | if (flags & PCI_MSI_FLAGS_MASKBIT) { |
| 314 | fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32, |
| 315 | pci_get_long(dev->config + msi_mask_off(dev, msi64bit)), |
| 316 | pci_get_long(dev->config + msi_pending_off(dev, msi64bit))); |
| 317 | } |
| 318 | fprintf(stderr, "\n"); |
| 319 | #endif |
| 320 | |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 321 | if (!(flags & PCI_MSI_FLAGS_ENABLE)) { |
| 322 | return; |
| 323 | } |
| 324 | |
| 325 | /* |
| 326 | * Now MSI is enabled, clear INTx# interrupts. |
| 327 | * the driver is prohibited from writing enable bit to mask |
| 328 | * a service request. But the guest OS could do this. |
| 329 | * So we just discard the interrupts as moderate fallback. |
| 330 | * |
| 331 | * 6.8.3.3. Enabling Operation |
| 332 | * While enabled for MSI or MSI-X operation, a function is prohibited |
| 333 | * from using its INTx# pin (if implemented) to request |
| 334 | * service (MSI, MSI-X, and INTx# are mutually exclusive). |
| 335 | */ |
Isaku Yamahata | 59369b0 | 2011-01-20 16:21:39 +0900 | [diff] [blame] | 336 | pci_device_deassert_intx(dev); |
Isaku Yamahata | e4c7d2a | 2010-10-19 18:06:32 +0900 | [diff] [blame] | 337 | |
| 338 | /* |
| 339 | * nr_vectors might be set bigger than capable. So clamp it. |
| 340 | * This is not legal by spec, so we can do anything we like, |
| 341 | * just don't crash the host |
| 342 | */ |
| 343 | log_num_vecs = |
| 344 | (flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1); |
| 345 | log_max_vecs = |
| 346 | (flags & PCI_MSI_FLAGS_QMASK) >> (ffs(PCI_MSI_FLAGS_QMASK) - 1); |
| 347 | if (log_num_vecs > log_max_vecs) { |
| 348 | flags &= ~PCI_MSI_FLAGS_QSIZE; |
| 349 | flags |= log_max_vecs << (ffs(PCI_MSI_FLAGS_QSIZE) - 1); |
| 350 | pci_set_word(dev->config + msi_flags_off(dev), flags); |
| 351 | } |
| 352 | |
| 353 | if (!msi_per_vector_mask) { |
| 354 | /* if per vector masking isn't supported, |
| 355 | there is no pending interrupt. */ |
| 356 | return; |
| 357 | } |
| 358 | |
| 359 | nr_vectors = msi_nr_vectors(flags); |
| 360 | |
| 361 | /* This will discard pending interrupts, if any. */ |
| 362 | pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit)); |
| 363 | pending &= 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors); |
| 364 | pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending); |
| 365 | |
| 366 | /* deliver pending interrupts which are unmasked */ |
| 367 | for (vector = 0; vector < nr_vectors; ++vector) { |
| 368 | if (msi_is_masked(dev, vector) || !(pending & (1U << vector))) { |
| 369 | continue; |
| 370 | } |
| 371 | |
| 372 | pci_long_test_and_clear_mask( |
| 373 | dev->config + msi_pending_off(dev, msi64bit), 1U << vector); |
| 374 | msi_notify(dev, vector); |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | unsigned int msi_nr_vectors_allocated(const PCIDevice *dev) |
| 379 | { |
| 380 | uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); |
| 381 | return msi_nr_vectors(flags); |
| 382 | } |