aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iommu/amd_iommu.c
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2014-07-03 11:51:24 -0400
committerJoerg Roedel <jroedel@suse.de>2014-07-04 06:35:58 -0400
commitc1931090a22b96b223f2a3b8420076f044da7531 (patch)
tree6b74b9b25e6edf33e631001e321061c435d37bf1 /drivers/iommu/amd_iommu.c
parent104a1c13ac66e40cf8c6ae74d76ff14ff24b9b01 (diff)
iommu/amd: Update to use PCI DMA aliases
AMD-Vi already has a concept of an alias provided via the IVRS table. Now that PCI-core also understands aliases, we need to incorporate both aspects when programming the IOMMU. IVRS is generally quite reliable, so we continue to prefer it when an alias is present. For cases where we have an IVRS alias that does not match the PCI alias or where PCI does not report an alias, report the mismatch to allow us to collect more quirks and dynamically incorporate the alias into the device alias quirks where possible. This should allow AMD-Vi to work with devices like Marvell and Ricoh with DMA function alias quirks unknown to the BIOS. Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Cc: Joerg Roedel <joro@8bytes.org> Signed-off-by: Joerg Roedel <jroedel@suse.de>
Diffstat (limited to 'drivers/iommu/amd_iommu.c')
-rw-r--r--drivers/iommu/amd_iommu.c78
1 files changed, 74 insertions, 4 deletions
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 4aec6a29e316..25d7571dfc1c 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -427,6 +427,68 @@ use_group:
427 return use_dev_data_iommu_group(dev_data->alias_data, dev); 427 return use_dev_data_iommu_group(dev_data->alias_data, dev);
428} 428}
429 429
430static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
431{
432 *(u16 *)data = alias;
433 return 0;
434}
435
436static u16 get_alias(struct device *dev)
437{
438 struct pci_dev *pdev = to_pci_dev(dev);
439 u16 devid, ivrs_alias, pci_alias;
440
441 devid = get_device_id(dev);
442 ivrs_alias = amd_iommu_alias_table[devid];
443 pci_for_each_dma_alias(pdev, __last_alias, &pci_alias);
444
445 if (ivrs_alias == pci_alias)
446 return ivrs_alias;
447
448 /*
449 * DMA alias showdown
450 *
451 * The IVRS is fairly reliable in telling us about aliases, but it
452 * can't know about every screwy device. If we don't have an IVRS
453 * reported alias, use the PCI reported alias. In that case we may
454 * still need to initialize the rlookup and dev_table entries if the
455 * alias is to a non-existent device.
456 */
457 if (ivrs_alias == devid) {
458 if (!amd_iommu_rlookup_table[pci_alias]) {
459 amd_iommu_rlookup_table[pci_alias] =
460 amd_iommu_rlookup_table[devid];
461 memcpy(amd_iommu_dev_table[pci_alias].data,
462 amd_iommu_dev_table[devid].data,
463 sizeof(amd_iommu_dev_table[pci_alias].data));
464 }
465
466 return pci_alias;
467 }
468
469 pr_info("AMD-Vi: Using IVRS reported alias %02x:%02x.%d "
470 "for device %s[%04x:%04x], kernel reported alias "
471 "%02x:%02x.%d\n", PCI_BUS_NUM(ivrs_alias), PCI_SLOT(ivrs_alias),
472 PCI_FUNC(ivrs_alias), dev_name(dev), pdev->vendor, pdev->device,
473 PCI_BUS_NUM(pci_alias), PCI_SLOT(pci_alias),
474 PCI_FUNC(pci_alias));
475
476 /*
477 * If we don't have a PCI DMA alias and the IVRS alias is on the same
478 * bus, then the IVRS table may know about a quirk that we don't.
479 */
480 if (pci_alias == devid &&
481 PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) {
482 pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
483 pdev->dma_alias_devfn = ivrs_alias & 0xff;
484 pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n",
485 PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias),
486 dev_name(dev));
487 }
488
489 return ivrs_alias;
490}
491
430static int iommu_init_device(struct device *dev) 492static int iommu_init_device(struct device *dev)
431{ 493{
432 struct pci_dev *pdev = to_pci_dev(dev); 494 struct pci_dev *pdev = to_pci_dev(dev);
@@ -441,7 +503,8 @@ static int iommu_init_device(struct device *dev)
441 if (!dev_data) 503 if (!dev_data)
442 return -ENOMEM; 504 return -ENOMEM;
443 505
444 alias = amd_iommu_alias_table[dev_data->devid]; 506 alias = get_alias(dev);
507
445 if (alias != dev_data->devid) { 508 if (alias != dev_data->devid) {
446 struct iommu_dev_data *alias_data; 509 struct iommu_dev_data *alias_data;
447 510
@@ -489,12 +552,19 @@ static void iommu_ignore_device(struct device *dev)
489 552
490static void iommu_uninit_device(struct device *dev) 553static void iommu_uninit_device(struct device *dev)
491{ 554{
555 struct iommu_dev_data *dev_data = search_dev_data(get_device_id(dev));
556
557 if (!dev_data)
558 return;
559
492 iommu_group_remove_device(dev); 560 iommu_group_remove_device(dev);
493 561
562 /* Unlink from alias, it may change if another device is re-plugged */
563 dev_data->alias_data = NULL;
564
494 /* 565 /*
495 * Nothing to do here - we keep dev_data around for unplugged devices 566 * We keep dev_data around for unplugged devices and reuse it when the
496 * and reuse it when the device is re-plugged - not doing so would 567 * device is re-plugged - not doing so would introduce a ton of races.
497 * introduce a ton of races.
498 */ 568 */
499} 569}
500 570