| .. | .. |
|---|
| 189 | 189 | {} |
|---|
| 190 | 190 | }; |
|---|
| 191 | 191 | |
|---|
| 192 | +static struct msi_msix_field_config { |
|---|
| 193 | + u16 enable_bit; /* bit for enabling MSI/MSI-X */ |
|---|
| 194 | + u16 allowed_bits; /* bits allowed to be changed */ |
|---|
| 195 | + unsigned int int_type; /* interrupt type for exclusiveness check */ |
|---|
| 196 | +} msi_field_config = { |
|---|
| 197 | + .enable_bit = PCI_MSI_FLAGS_ENABLE, |
|---|
| 198 | + .allowed_bits = PCI_MSI_FLAGS_ENABLE, |
|---|
| 199 | + .int_type = INTERRUPT_TYPE_MSI, |
|---|
| 200 | +}, msix_field_config = { |
|---|
| 201 | + .enable_bit = PCI_MSIX_FLAGS_ENABLE, |
|---|
| 202 | + .allowed_bits = PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL, |
|---|
| 203 | + .int_type = INTERRUPT_TYPE_MSIX, |
|---|
| 204 | +}; |
|---|
| 205 | + |
|---|
| 206 | +static void *msi_field_init(struct pci_dev *dev, int offset) |
|---|
| 207 | +{ |
|---|
| 208 | + return &msi_field_config; |
|---|
| 209 | +} |
|---|
| 210 | + |
|---|
| 211 | +static void *msix_field_init(struct pci_dev *dev, int offset) |
|---|
| 212 | +{ |
|---|
| 213 | + return &msix_field_config; |
|---|
| 214 | +} |
|---|
| 215 | + |
|---|
| 216 | +static int msi_msix_flags_write(struct pci_dev *dev, int offset, u16 new_value, |
|---|
| 217 | + void *data) |
|---|
| 218 | +{ |
|---|
| 219 | + int err; |
|---|
| 220 | + u16 old_value; |
|---|
| 221 | + const struct msi_msix_field_config *field_config = data; |
|---|
| 222 | + const struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); |
|---|
| 223 | + |
|---|
| 224 | + if (xen_pcibk_permissive || dev_data->permissive) |
|---|
| 225 | + goto write; |
|---|
| 226 | + |
|---|
| 227 | + err = pci_read_config_word(dev, offset, &old_value); |
|---|
| 228 | + if (err) |
|---|
| 229 | + return err; |
|---|
| 230 | + |
|---|
| 231 | + if (new_value == old_value) |
|---|
| 232 | + return 0; |
|---|
| 233 | + |
|---|
| 234 | + if (!dev_data->allow_interrupt_control || |
|---|
| 235 | + (new_value ^ old_value) & ~field_config->allowed_bits) |
|---|
| 236 | + return PCIBIOS_SET_FAILED; |
|---|
| 237 | + |
|---|
| 238 | + if (new_value & field_config->enable_bit) { |
|---|
| 239 | + /* don't allow enabling together with other interrupt types */ |
|---|
| 240 | + int int_type = xen_pcibk_get_interrupt_type(dev); |
|---|
| 241 | + |
|---|
| 242 | + if (int_type == INTERRUPT_TYPE_NONE || |
|---|
| 243 | + int_type == field_config->int_type) |
|---|
| 244 | + goto write; |
|---|
| 245 | + return PCIBIOS_SET_FAILED; |
|---|
| 246 | + } |
|---|
| 247 | + |
|---|
| 248 | +write: |
|---|
| 249 | + return pci_write_config_word(dev, offset, new_value); |
|---|
| 250 | +} |
|---|
| 251 | + |
|---|
| 252 | +static const struct config_field caplist_msix[] = { |
|---|
| 253 | + { |
|---|
| 254 | + .offset = PCI_MSIX_FLAGS, |
|---|
| 255 | + .size = 2, |
|---|
| 256 | + .init = msix_field_init, |
|---|
| 257 | + .u.w.read = xen_pcibk_read_config_word, |
|---|
| 258 | + .u.w.write = msi_msix_flags_write, |
|---|
| 259 | + }, |
|---|
| 260 | + {} |
|---|
| 261 | +}; |
|---|
| 262 | + |
|---|
| 263 | +static const struct config_field caplist_msi[] = { |
|---|
| 264 | + { |
|---|
| 265 | + .offset = PCI_MSI_FLAGS, |
|---|
| 266 | + .size = 2, |
|---|
| 267 | + .init = msi_field_init, |
|---|
| 268 | + .u.w.read = xen_pcibk_read_config_word, |
|---|
| 269 | + .u.w.write = msi_msix_flags_write, |
|---|
| 270 | + }, |
|---|
| 271 | + {} |
|---|
| 272 | +}; |
|---|
| 273 | + |
|---|
| 192 | 274 | static struct xen_pcibk_config_capability xen_pcibk_config_capability_pm = { |
|---|
| 193 | 275 | .capability = PCI_CAP_ID_PM, |
|---|
| 194 | 276 | .fields = caplist_pm, |
|---|
| .. | .. |
|---|
| 197 | 279 | .capability = PCI_CAP_ID_VPD, |
|---|
| 198 | 280 | .fields = caplist_vpd, |
|---|
| 199 | 281 | }; |
|---|
| 282 | +static struct xen_pcibk_config_capability xen_pcibk_config_capability_msi = { |
|---|
| 283 | + .capability = PCI_CAP_ID_MSI, |
|---|
| 284 | + .fields = caplist_msi, |
|---|
| 285 | +}; |
|---|
| 286 | +static struct xen_pcibk_config_capability xen_pcibk_config_capability_msix = { |
|---|
| 287 | + .capability = PCI_CAP_ID_MSIX, |
|---|
| 288 | + .fields = caplist_msix, |
|---|
| 289 | +}; |
|---|
| 200 | 290 | |
|---|
| 201 | 291 | int xen_pcibk_config_capability_init(void) |
|---|
| 202 | 292 | { |
|---|
| 203 | 293 | register_capability(&xen_pcibk_config_capability_vpd); |
|---|
| 204 | 294 | register_capability(&xen_pcibk_config_capability_pm); |
|---|
| 295 | + register_capability(&xen_pcibk_config_capability_msi); |
|---|
| 296 | + register_capability(&xen_pcibk_config_capability_msix); |
|---|
| 205 | 297 | |
|---|
| 206 | 298 | return 0; |
|---|
| 207 | 299 | } |
|---|