Commit c1931090 authored by Alex Williamson's avatar Alex Williamson Committed by Joerg Roedel

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: default avatarAlex Williamson <alex.williamson@redhat.com>
Cc: Joerg Roedel <joro@8bytes.org>
Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent 104a1c13
...@@ -427,6 +427,68 @@ static int init_iommu_group(struct device *dev) ...@@ -427,6 +427,68 @@ static int init_iommu_group(struct device *dev)
return use_dev_data_iommu_group(dev_data->alias_data, dev); return use_dev_data_iommu_group(dev_data->alias_data, dev);
} }
static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
{
*(u16 *)data = alias;
return 0;
}
static u16 get_alias(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
u16 devid, ivrs_alias, pci_alias;
devid = get_device_id(dev);
ivrs_alias = amd_iommu_alias_table[devid];
pci_for_each_dma_alias(pdev, __last_alias, &pci_alias);
if (ivrs_alias == pci_alias)
return ivrs_alias;
/*
* DMA alias showdown
*
* The IVRS is fairly reliable in telling us about aliases, but it
* can't know about every screwy device. If we don't have an IVRS
* reported alias, use the PCI reported alias. In that case we may
* still need to initialize the rlookup and dev_table entries if the
* alias is to a non-existent device.
*/
if (ivrs_alias == devid) {
if (!amd_iommu_rlookup_table[pci_alias]) {
amd_iommu_rlookup_table[pci_alias] =
amd_iommu_rlookup_table[devid];
memcpy(amd_iommu_dev_table[pci_alias].data,
amd_iommu_dev_table[devid].data,
sizeof(amd_iommu_dev_table[pci_alias].data));
}
return pci_alias;
}
pr_info("AMD-Vi: Using IVRS reported alias %02x:%02x.%d "
"for device %s[%04x:%04x], kernel reported alias "
"%02x:%02x.%d\n", PCI_BUS_NUM(ivrs_alias), PCI_SLOT(ivrs_alias),
PCI_FUNC(ivrs_alias), dev_name(dev), pdev->vendor, pdev->device,
PCI_BUS_NUM(pci_alias), PCI_SLOT(pci_alias),
PCI_FUNC(pci_alias));
/*
* If we don't have a PCI DMA alias and the IVRS alias is on the same
* bus, then the IVRS table may know about a quirk that we don't.
*/
if (pci_alias == devid &&
PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) {
pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
pdev->dma_alias_devfn = ivrs_alias & 0xff;
pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n",
PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias),
dev_name(dev));
}
return ivrs_alias;
}
static int iommu_init_device(struct device *dev) static int iommu_init_device(struct device *dev)
{ {
struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev *pdev = to_pci_dev(dev);
...@@ -441,7 +503,8 @@ static int iommu_init_device(struct device *dev) ...@@ -441,7 +503,8 @@ static int iommu_init_device(struct device *dev)
if (!dev_data) if (!dev_data)
return -ENOMEM; return -ENOMEM;
alias = amd_iommu_alias_table[dev_data->devid]; alias = get_alias(dev);
if (alias != dev_data->devid) { if (alias != dev_data->devid) {
struct iommu_dev_data *alias_data; struct iommu_dev_data *alias_data;
...@@ -489,12 +552,19 @@ static void iommu_ignore_device(struct device *dev) ...@@ -489,12 +552,19 @@ static void iommu_ignore_device(struct device *dev)
static void iommu_uninit_device(struct device *dev) static void iommu_uninit_device(struct device *dev)
{ {
struct iommu_dev_data *dev_data = search_dev_data(get_device_id(dev));
if (!dev_data)
return;
iommu_group_remove_device(dev); iommu_group_remove_device(dev);
/* Unlink from alias, it may change if another device is re-plugged */
dev_data->alias_data = NULL;
/* /*
* Nothing to do here - we keep dev_data around for unplugged devices * We keep dev_data around for unplugged devices and reuse it when the
* and reuse it when the device is re-plugged - not doing so would * device is re-plugged - not doing so would introduce a ton of races.
* introduce a ton of races.
*/ */
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment