Skip to content

Commit c193109

Browse files
Alex Williamsonjoergroedel
authored andcommitted
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>
1 parent 104a1c1 commit c193109

File tree

1 file changed

+74
-4
lines changed

1 file changed

+74
-4
lines changed

drivers/iommu/amd_iommu.c

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,68 @@ static int init_iommu_group(struct device *dev)
427427
return use_dev_data_iommu_group(dev_data->alias_data, dev);
428428
}
429429

430+
static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
431+
{
432+
*(u16 *)data = alias;
433+
return 0;
434+
}
435+
436+
static 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+
430492
static int iommu_init_device(struct device *dev)
431493
{
432494
struct pci_dev *pdev = to_pci_dev(dev);
@@ -441,7 +503,8 @@ static int iommu_init_device(struct device *dev)
441503
if (!dev_data)
442504
return -ENOMEM;
443505

444-
alias = amd_iommu_alias_table[dev_data->devid];
506+
alias = get_alias(dev);
507+
445508
if (alias != dev_data->devid) {
446509
struct iommu_dev_data *alias_data;
447510

@@ -489,12 +552,19 @@ static void iommu_ignore_device(struct device *dev)
489552

490553
static void iommu_uninit_device(struct device *dev)
491554
{
555+
struct iommu_dev_data *dev_data = search_dev_data(get_device_id(dev));
556+
557+
if (!dev_data)
558+
return;
559+
492560
iommu_group_remove_device(dev);
493561

562+
/* Unlink from alias, it may change if another device is re-plugged */
563+
dev_data->alias_data = NULL;
564+
494565
/*
495-
* Nothing to do here - we keep dev_data around for unplugged devices
496-
* and reuse it when the device is re-plugged - not doing so would
497-
* introduce a ton of races.
566+
* We keep dev_data around for unplugged devices and reuse it when the
567+
* device is re-plugged - not doing so would introduce a ton of races.
498568
*/
499569
}
500570

0 commit comments

Comments
 (0)