Commit cbb24a25 authored by Joerg Roedel's avatar Joerg Roedel

Merge branch 'core' into x86/vt-d

Conflicts:
	drivers/iommu/intel-iommu.c
parents aa4d066a e09f8ea5
What: /sys/class/iommu/<iommu>/devices/
Date: June 2014
KernelVersion: 3.17
Contact: Alex Williamson <alex.williamson@redhat.com>
Description:
IOMMU drivers are able to link devices managed by a
given IOMMU here to allow association of IOMMU to
device.
What: /sys/devices/.../iommu
Date: June 2014
KernelVersion: 3.17
Contact: Alex Williamson <alex.williamson@redhat.com>
Description:
IOMMU drivers are able to link the IOMMU for a
given device here to allow association of device to
IOMMU.
What: /sys/class/iommu/<iommu>/amd-iommu/cap
Date: June 2014
KernelVersion: 3.17
Contact: Alex Williamson <alex.williamson@redhat.com>
Description:
IOMMU capability header as documented in the AMD IOMMU
specification. Format: %x
What: /sys/class/iommu/<iommu>/amd-iommu/features
Date: June 2014
KernelVersion: 3.17
Contact: Alex Williamson <alex.williamson@redhat.com>
Description:
Extended features of the IOMMU. Format: %llx
What: /sys/class/iommu/<iommu>/intel-iommu/address
Date: June 2014
KernelVersion: 3.17
Contact: Alex Williamson <alex.williamson@redhat.com>
Description:
Physical address of the VT-d DRHD for this IOMMU.
Format: %llx. This allows association of a sysfs
intel-iommu with a DMAR DRHD table entry.
What: /sys/class/iommu/<iommu>/intel-iommu/cap
Date: June 2014
KernelVersion: 3.17
Contact: Alex Williamson <alex.williamson@redhat.com>
Description:
The cached hardware capability register value
of this DRHD unit. Format: %llx.
What: /sys/class/iommu/<iommu>/intel-iommu/ecap
Date: June 2014
KernelVersion: 3.17
Contact: Alex Williamson <alex.williamson@redhat.com>
Description:
The cached hardware extended capability register
value of this DRHD unit. Format: %llx.
What: /sys/class/iommu/<iommu>/intel-iommu/version
Date: June 2014
KernelVersion: 3.17
Contact: Alex Williamson <alex.williamson@redhat.com>
Description:
The architecture version as reported from the
VT-d VER_REG. Format: %d:%d, major:minor
obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_API) += iommu.o
obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o
obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
obj-$(CONFIG_OF_IOMMU) += of_iommu.o obj-$(CONFIG_OF_IOMMU) += of_iommu.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
......
...@@ -46,7 +46,6 @@ ...@@ -46,7 +46,6 @@
#include "amd_iommu_proto.h" #include "amd_iommu_proto.h"
#include "amd_iommu_types.h" #include "amd_iommu_types.h"
#include "irq_remapping.h" #include "irq_remapping.h"
#include "pci.h"
#define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28)) #define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28))
...@@ -81,7 +80,7 @@ LIST_HEAD(hpet_map); ...@@ -81,7 +80,7 @@ LIST_HEAD(hpet_map);
*/ */
static struct protection_domain *pt_domain; static struct protection_domain *pt_domain;
static struct iommu_ops amd_iommu_ops; static const struct iommu_ops amd_iommu_ops;
static ATOMIC_NOTIFIER_HEAD(ppr_notifier); static ATOMIC_NOTIFIER_HEAD(ppr_notifier);
int amd_iommu_max_glx_val = -1; int amd_iommu_max_glx_val = -1;
...@@ -133,9 +132,6 @@ static void free_dev_data(struct iommu_dev_data *dev_data) ...@@ -133,9 +132,6 @@ static void free_dev_data(struct iommu_dev_data *dev_data)
list_del(&dev_data->dev_data_list); list_del(&dev_data->dev_data_list);
spin_unlock_irqrestore(&dev_data_list_lock, flags); spin_unlock_irqrestore(&dev_data_list_lock, flags);
if (dev_data->group)
iommu_group_put(dev_data->group);
kfree(dev_data); kfree(dev_data);
} }
...@@ -264,167 +260,79 @@ static bool check_device(struct device *dev) ...@@ -264,167 +260,79 @@ static bool check_device(struct device *dev)
return true; return true;
} }
static struct pci_bus *find_hosted_bus(struct pci_bus *bus) static int init_iommu_group(struct device *dev)
{
while (!bus->self) {
if (!pci_is_root_bus(bus))
bus = bus->parent;
else
return ERR_PTR(-ENODEV);
}
return bus;
}
#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
static struct pci_dev *get_isolation_root(struct pci_dev *pdev)
{
struct pci_dev *dma_pdev = pdev;
/* Account for quirked devices */
swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
/*
* If it's a multifunction device that does not support our
* required ACS flags, add to the same group as lowest numbered
* function that also does not suport the required ACS flags.
*/
if (dma_pdev->multifunction &&
!pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) {
u8 i, slot = PCI_SLOT(dma_pdev->devfn);
for (i = 0; i < 8; i++) {
struct pci_dev *tmp;
tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i));
if (!tmp)
continue;
if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) {
swap_pci_ref(&dma_pdev, tmp);
break;
}
pci_dev_put(tmp);
}
}
/*
* Devices on the root bus go through the iommu. If that's not us,
* find the next upstream device and test ACS up to the root bus.
* Finding the next device may require skipping virtual buses.
*/
while (!pci_is_root_bus(dma_pdev->bus)) {
struct pci_bus *bus = find_hosted_bus(dma_pdev->bus);
if (IS_ERR(bus))
break;
if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
break;
swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
}
return dma_pdev;
}
static int use_pdev_iommu_group(struct pci_dev *pdev, struct device *dev)
{ {
struct iommu_group *group = iommu_group_get(&pdev->dev); struct iommu_group *group;
int ret;
if (!group) { group = iommu_group_get_for_dev(dev);
group = iommu_group_alloc();
if (IS_ERR(group))
return PTR_ERR(group);
WARN_ON(&pdev->dev != dev); if (IS_ERR(group))
} return PTR_ERR(group);
ret = iommu_group_add_device(group, dev);
iommu_group_put(group); iommu_group_put(group);
return ret; return 0;
} }
static int use_dev_data_iommu_group(struct iommu_dev_data *dev_data, static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
struct device *dev)
{ {
if (!dev_data->group) { *(u16 *)data = alias;
struct iommu_group *group = iommu_group_alloc(); return 0;
if (IS_ERR(group))
return PTR_ERR(group);
dev_data->group = group;
}
return iommu_group_add_device(dev_data->group, dev);
} }
static int init_iommu_group(struct device *dev) static u16 get_alias(struct device *dev)
{ {
struct iommu_dev_data *dev_data; struct pci_dev *pdev = to_pci_dev(dev);
struct iommu_group *group; u16 devid, ivrs_alias, pci_alias;
struct pci_dev *dma_pdev;
int ret;
group = iommu_group_get(dev);
if (group) {
iommu_group_put(group);
return 0;
}
dev_data = find_dev_data(get_device_id(dev));
if (!dev_data)
return -ENOMEM;
if (dev_data->alias_data) { devid = get_device_id(dev);
u16 alias; ivrs_alias = amd_iommu_alias_table[devid];
struct pci_bus *bus; pci_for_each_dma_alias(pdev, __last_alias, &pci_alias);
if (dev_data->alias_data->group) if (ivrs_alias == pci_alias)
goto use_group; return ivrs_alias;
/* /*
* If the alias device exists, it's effectively just a first * DMA alias showdown
* level quirk for finding the DMA source. *
*/ * The IVRS is fairly reliable in telling us about aliases, but it
alias = amd_iommu_alias_table[dev_data->devid]; * can't know about every screwy device. If we don't have an IVRS
dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff); * reported alias, use the PCI reported alias. In that case we may
if (dma_pdev) { * still need to initialize the rlookup and dev_table entries if the
dma_pdev = get_isolation_root(dma_pdev); * alias is to a non-existent device.
goto use_pdev; */
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;
* If the alias is virtual, try to find a parent device }
* and test whether the IOMMU group is actualy rooted above
* the alias. Be careful to also test the parent device if
* we think the alias is the root of the group.
*/
bus = pci_find_bus(0, alias >> 8);
if (!bus)
goto use_group;
bus = find_hosted_bus(bus);
if (IS_ERR(bus) || !bus->self)
goto use_group;
dma_pdev = get_isolation_root(pci_dev_get(bus->self)); pr_info("AMD-Vi: Using IVRS reported alias %02x:%02x.%d "
if (dma_pdev != bus->self || (dma_pdev->multifunction && "for device %s[%04x:%04x], kernel reported alias "
!pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))) "%02x:%02x.%d\n", PCI_BUS_NUM(ivrs_alias), PCI_SLOT(ivrs_alias),
goto use_pdev; PCI_FUNC(ivrs_alias), dev_name(dev), pdev->vendor, pdev->device,
PCI_BUS_NUM(pci_alias), PCI_SLOT(pci_alias),
PCI_FUNC(pci_alias));
pci_dev_put(dma_pdev); /*
goto use_group; * 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));
} }
dma_pdev = get_isolation_root(pci_dev_get(to_pci_dev(dev))); return ivrs_alias;
use_pdev:
ret = use_pdev_iommu_group(dma_pdev, dev);
pci_dev_put(dma_pdev);
return ret;
use_group:
return use_dev_data_iommu_group(dev_data->alias_data, dev);
} }
static int iommu_init_device(struct device *dev) static int iommu_init_device(struct device *dev)
...@@ -441,7 +349,8 @@ static int iommu_init_device(struct device *dev) ...@@ -441,7 +349,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;
...@@ -470,6 +379,9 @@ static int iommu_init_device(struct device *dev) ...@@ -470,6 +379,9 @@ static int iommu_init_device(struct device *dev)
dev->archdata.iommu = dev_data; dev->archdata.iommu = dev_data;
iommu_device_link(amd_iommu_rlookup_table[dev_data->devid]->iommu_dev,
dev);
return 0; return 0;
} }
...@@ -489,12 +401,22 @@ static void iommu_ignore_device(struct device *dev) ...@@ -489,12 +401,22 @@ 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_device_unlink(amd_iommu_rlookup_table[dev_data->devid]->iommu_dev,
dev);
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.
*/ */
} }
...@@ -3473,7 +3395,7 @@ static int amd_iommu_domain_has_cap(struct iommu_domain *domain, ...@@ -3473,7 +3395,7 @@ static int amd_iommu_domain_has_cap(struct iommu_domain *domain,
return 0; return 0;
} }
static struct iommu_ops amd_iommu_ops = { static const struct iommu_ops amd_iommu_ops = {
.domain_init = amd_iommu_domain_init, .domain_init = amd_iommu_domain_init,
.domain_destroy = amd_iommu_domain_destroy, .domain_destroy = amd_iommu_domain_destroy,
.attach_dev = amd_iommu_attach_device, .attach_dev = amd_iommu_attach_device,
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/msi.h> #include <linux/msi.h>
#include <linux/amd-iommu.h> #include <linux/amd-iommu.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/iommu.h>
#include <asm/pci-direct.h> #include <asm/pci-direct.h>
#include <asm/iommu.h> #include <asm/iommu.h>
#include <asm/gart.h> #include <asm/gart.h>
...@@ -1197,6 +1198,39 @@ static void init_iommu_perf_ctr(struct amd_iommu *iommu) ...@@ -1197,6 +1198,39 @@ static void init_iommu_perf_ctr(struct amd_iommu *iommu)
iommu->max_counters = (u8) ((val >> 7) & 0xf); iommu->max_counters = (u8) ((val >> 7) & 0xf);
} }
static ssize_t amd_iommu_show_cap(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct amd_iommu *iommu = dev_get_drvdata(dev);
return sprintf(buf, "%x\n", iommu->cap);
}
static DEVICE_ATTR(cap, S_IRUGO, amd_iommu_show_cap, NULL);
static ssize_t amd_iommu_show_features(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct amd_iommu *iommu = dev_get_drvdata(dev);
return sprintf(buf, "%llx\n", iommu->features);
}
static DEVICE_ATTR(features, S_IRUGO, amd_iommu_show_features, NULL);
static struct attribute *amd_iommu_attrs[] = {
&dev_attr_cap.attr,
&dev_attr_features.attr,
NULL,
};
static struct attribute_group amd_iommu_group = {
.name = "amd-iommu",
.attrs = amd_iommu_attrs,
};
static const struct attribute_group *amd_iommu_groups[] = {
&amd_iommu_group,
NULL,
};
static int iommu_init_pci(struct amd_iommu *iommu) static int iommu_init_pci(struct amd_iommu *iommu)
{ {
...@@ -1297,6 +1331,10 @@ static int iommu_init_pci(struct amd_iommu *iommu) ...@@ -1297,6 +1331,10 @@ static int iommu_init_pci(struct amd_iommu *iommu)
amd_iommu_erratum_746_workaround(iommu); amd_iommu_erratum_746_workaround(iommu);
iommu->iommu_dev = iommu_device_create(&iommu->dev->dev, iommu,
amd_iommu_groups, "ivhd%d",
iommu->index);
return pci_enable_device(iommu->dev); return pci_enable_device(iommu->dev);
} }
......
...@@ -432,7 +432,6 @@ struct iommu_dev_data { ...@@ -432,7 +432,6 @@ struct iommu_dev_data {
struct iommu_dev_data *alias_data;/* The alias dev_data */ struct iommu_dev_data *alias_data;/* The alias dev_data */
struct protection_domain *domain; /* Domain the device is bound to */ struct protection_domain *domain; /* Domain the device is bound to */
atomic_t bind; /* Domain attach reference count */ atomic_t bind; /* Domain attach reference count */
struct iommu_group *group; /* IOMMU group for virtual aliases */
u16 devid; /* PCI Device ID */ u16 devid; /* PCI Device ID */
bool iommu_v2; /* Device can make use of IOMMUv2 */ bool iommu_v2; /* Device can make use of IOMMUv2 */
bool passthrough; /* Default for device is pt_domain */ bool passthrough; /* Default for device is pt_domain */
...@@ -578,6 +577,9 @@ struct amd_iommu { ...@@ -578,6 +577,9 @@ struct amd_iommu {
/* default dma_ops domain for that IOMMU */ /* default dma_ops domain for that IOMMU */
struct dma_ops_domain *default_dom; struct dma_ops_domain *default_dom;
/* IOMMU sysfs device */
struct device *iommu_dev;
/* /*
* We can't rely on the BIOS to restore all values on reinit, so we * We can't rely on the BIOS to restore all values on reinit, so we
* need to stash them * need to stash them
......
...@@ -1609,7 +1609,7 @@ static void arm_smmu_remove_device(struct device *dev) ...@@ -1609,7 +1609,7 @@ static void arm_smmu_remove_device(struct device *dev)
iommu_group_remove_device(dev); iommu_group_remove_device(dev);
} }
static struct iommu_ops arm_smmu_ops = { static const struct iommu_ops arm_smmu_ops = {
.domain_init = arm_smmu_domain_init, .domain_init = arm_smmu_domain_init,
.domain_destroy = arm_smmu_domain_destroy, .domain_destroy = arm_smmu_domain_destroy,
.attach_dev = arm_smmu_attach_dev, .attach_dev = arm_smmu_attach_dev,
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <linux/tboot.h> #include <linux/tboot.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/iommu.h>
#include <asm/irq_remapping.h> #include <asm/irq_remapping.h>
#include <asm/iommu_table.h> #include <asm/iommu_table.h>
...@@ -980,6 +981,12 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd) ...@@ -980,6 +981,12 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
raw_spin_lock_init(&iommu->register_lock); raw_spin_lock_init(&iommu->register_lock);
drhd->iommu = iommu; drhd->iommu = iommu;
if (intel_iommu_enabled)
iommu->iommu_dev = iommu_device_create(NULL, iommu,
intel_iommu_groups,
iommu->name);
return 0; return 0;
err_unmap: err_unmap:
...@@ -991,6 +998,8 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd) ...@@ -991,6 +998,8 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
static void free_iommu(struct intel_iommu *iommu) static void free_iommu(struct intel_iommu *iommu)
{ {
iommu_device_destroy(iommu->iommu_dev);
if (iommu->irq) { if (iommu->irq) {
free_irq(iommu->irq, iommu); free_irq(iommu->irq, iommu);
irq_set_handler_data(iommu->irq, NULL); irq_set_handler_data(iommu->irq, NULL);
......
...@@ -1170,7 +1170,7 @@ static void exynos_iommu_remove_device(struct device *dev) ...@@ -1170,7 +1170,7 @@ static void exynos_iommu_remove_device(struct device *dev)
iommu_group_remove_device(dev); iommu_group_remove_device(dev);
} }
static struct iommu_ops exynos_iommu_ops = { static const struct iommu_ops exynos_iommu_ops = {
.domain_init = exynos_iommu_domain_init, .domain_init = exynos_iommu_domain_init,
.domain_destroy = exynos_iommu_domain_destroy, .domain_destroy = exynos_iommu_domain_destroy,
.attach_dev = exynos_iommu_attach_device, .attach_dev = exynos_iommu_attach_device,
......
...@@ -38,7 +38,6 @@ ...@@ -38,7 +38,6 @@
#include <sysdev/fsl_pci.h> #include <sysdev/fsl_pci.h>
#include "fsl_pamu_domain.h" #include "fsl_pamu_domain.h"
#include "pci.h"
/* /*
* Global spinlock that needs to be held while * Global spinlock that needs to be held while
...@@ -892,8 +891,6 @@ static int fsl_pamu_get_domain_attr(struct iommu_domain *domain, ...@@ -892,8 +891,6 @@ static int fsl_pamu_get_domain_attr(struct iommu_domain *domain,
return ret; return ret;
} }
#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
static struct iommu_group *get_device_iommu_group(struct device *dev) static struct iommu_group *get_device_iommu_group(struct device *dev)
{ {
struct iommu_group *group; struct iommu_group *group;
...@@ -950,74 +947,13 @@ static struct iommu_group *get_pci_device_group(struct pci_dev *pdev) ...@@ -950,74 +947,13 @@ static struct iommu_group *get_pci_device_group(struct pci_dev *pdev)
struct pci_controller *pci_ctl; struct pci_controller *pci_ctl;
bool pci_endpt_partioning; bool pci_endpt_partioning;
struct iommu_group *group = NULL; struct iommu_group *group = NULL;
struct pci_dev *bridge, *dma_pdev = NULL;
pci_ctl = pci_bus_to_host(pdev->bus); pci_ctl = pci_bus_to_host(pdev->bus);
pci_endpt_partioning = check_pci_ctl_endpt_part(pci_ctl); pci_endpt_partioning = check_pci_ctl_endpt_part(pci_ctl);
/* We can partition PCIe devices so assign device group to the device */ /* We can partition PCIe devices so assign device group to the device */
if (pci_endpt_partioning) { if (pci_endpt_partioning) {
bridge = pci_find_upstream_pcie_bridge(pdev); group = iommu_group_get_for_dev(&pdev->dev);
if (bridge) {
if (pci_is_pcie(bridge))
dma_pdev = pci_get_domain_bus_and_slot(
pci_domain_nr(pdev->bus),
bridge->subordinate->number, 0);
if (!dma_pdev)
dma_pdev = pci_dev_get(bridge);
} else
dma_pdev = pci_dev_get(pdev);
/* Account for quirked devices */
swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
/*
* If it's a multifunction device that does not support our
* required ACS flags, add to the same group as lowest numbered
* function that also does not suport the required ACS flags.
*/
if (dma_pdev->multifunction &&
!pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) {
u8 i, slot = PCI_SLOT(dma_pdev->devfn);
for (i = 0; i < 8; i++) {
struct pci_dev *tmp;
tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i));
if (!tmp)
continue;
if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) {
swap_pci_ref(&dma_pdev, tmp);
break;
}
pci_dev_put(tmp);
}
}
/*
* Devices on the root bus go through the iommu. If that's not us,
* find the next upstream device and test ACS up to the root bus.
* Finding the next device may require skipping virtual buses.
*/
while (!pci_is_root_bus(dma_pdev->bus)) {
struct pci_bus *bus = dma_pdev->bus;
while (!bus->self) {
if (!pci_is_root_bus(bus))
bus = bus->parent;
else
goto root_bus;
}
if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
break;
swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
}
root_bus:
group = get_device_iommu_group(&dma_pdev->dev);
pci_dev_put(dma_pdev);
/* /*
* PCIe controller is not a paritionable entity * PCIe controller is not a paritionable entity
* free the controller device iommu_group. * free the controller device iommu_group.
...@@ -1140,7 +1076,7 @@ static u32 fsl_pamu_get_windows(struct iommu_domain *domain) ...@@ -1140,7 +1076,7 @@ static u32 fsl_pamu_get_windows(struct iommu_domain *domain)
return dma_domain->win_cnt; return dma_domain->win_cnt;
} }
static struct iommu_ops fsl_pamu_ops = { static const struct iommu_ops fsl_pamu_ops = {
.domain_init = fsl_pamu_domain_init, .domain_init = fsl_pamu_domain_init,
.domain_destroy = fsl_pamu_domain_destroy, .domain_destroy = fsl_pamu_domain_destroy,
.attach_dev = fsl_pamu_attach_device, .attach_dev = fsl_pamu_attach_device,
......
This diff is collapsed.
...@@ -369,29 +369,52 @@ static int set_hpet_sid(struct irte *irte, u8 id) ...@@ -369,29 +369,52 @@ static int set_hpet_sid(struct irte *irte, u8 id)
return 0; return 0;
} }
struct set_msi_sid_data {
struct pci_dev *pdev;
u16 alias;
};
static int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque)
{
struct set_msi_sid_data *data = opaque;
data->pdev = pdev;
data->alias = alias;
return 0;
}
static int set_msi_sid(struct irte *irte, struct pci_dev *dev) static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
{ {
struct pci_dev *bridge; struct set_msi_sid_data data;
if (!irte || !dev) if (!irte || !dev)
return -1; return -1;
/* PCIe device or Root Complex integrated PCI device */ pci_for_each_dma_alias(dev, set_msi_sid_cb, &data);
if (pci_is_pcie(dev) || !dev->bus->parent) {
set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
(dev->bus->number << 8) | dev->devfn);
return 0;
}
bridge = pci_find_upstream_pcie_bridge(dev); /*
if (bridge) { * DMA alias provides us with a PCI device and alias. The only case
if (pci_is_pcie(bridge))/* this is a PCIe-to-PCI/PCIX bridge */ * where the it will return an alias on a different bus than the
set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16, * device is the case of a PCIe-to-PCI bridge, where the alias is for
(bridge->bus->number << 8) | dev->bus->number); * the subordinate bus. In this case we can only verify the bus.
else /* this is a legacy PCI bridge */ *
set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, * If the alias device is on a different bus than our source device
(bridge->bus->number << 8) | bridge->devfn); * then we have a topology based alias, use it.
} *
* Otherwise, the alias is for a device DMA quirk and we cannot
* assume that MSI uses the same requester ID. Therefore use the
* original device.
*/
if (PCI_BUS_NUM(data.alias) != data.pdev->bus->number)
set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
PCI_DEVID(PCI_BUS_NUM(data.alias),
dev->bus->number));
else if (data.pdev->bus->number != dev->bus->number)
set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, data.alias);
else
set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
PCI_DEVID(dev->bus->number, dev->devfn));
return 0; return 0;
} }
......
/*
* IOMMU sysfs class support
*
* Copyright (C) 2014 Red Hat, Inc. All rights reserved.
* Author: Alex Williamson <alex.williamson@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/device.h>
#include <linux/iommu.h>
#include <linux/module.h>
#include <linux/slab.h>
/*
* We provide a common class "devices" group which initially has no attributes.
* As devices are added to the IOMMU, we'll add links to the group.
*/
static struct attribute *devices_attr[] = {
NULL,
};
static const struct attribute_group iommu_devices_attr_group = {
.name = "devices",
.attrs = devices_attr,
};
static const struct attribute_group *iommu_dev_groups[] = {
&iommu_devices_attr_group,
NULL,
};
static void iommu_release_device(struct device *dev)
{
kfree(dev);
}
static struct class iommu_class = {
.name = "iommu",
.dev_release = iommu_release_device,
.dev_groups = iommu_dev_groups,
};
static int __init iommu_dev_init(void)
{
return class_register(&iommu_class);
}
postcore_initcall(iommu_dev_init);
/*
* Create an IOMMU device and return a pointer to it. IOMMU specific
* attributes can be provided as an attribute group, allowing a unique
* namespace per IOMMU type.
*/
struct device *iommu_device_create(struct device *parent, void *drvdata,
const struct attribute_group **groups,
const char *fmt, ...)
{
struct device *dev;
va_list vargs;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
device_initialize(dev);
dev->class = &iommu_class;
dev->parent = parent;
dev->groups = groups;
dev_set_drvdata(dev, drvdata);
va_start(vargs, fmt);
ret = kobject_set_name_vargs(&dev->kobj, fmt, vargs);
va_end(vargs);
if (ret)
goto error;
ret = device_add(dev);
if (ret)
goto error;
return dev;
error:
put_device(dev);
return ERR_PTR(ret);
}
void iommu_device_destroy(struct device *dev)
{
if (!dev || IS_ERR(dev))
return;
device_unregister(dev);
}
/*
* IOMMU drivers can indicate a device is managed by a given IOMMU using
* this interface. A link to the device will be created in the "devices"
* directory of the IOMMU device in sysfs and an "iommu" link will be
* created under the linked device, pointing back at the IOMMU device.
*/
int iommu_device_link(struct device *dev, struct device *link)
{
int ret;
if (!dev || IS_ERR(dev))
return -ENODEV;
ret = sysfs_add_link_to_group(&dev->kobj, "devices",
&link->kobj, dev_name(link));
if (ret)
return ret;
ret = sysfs_create_link_nowarn(&link->kobj, &dev->kobj, "iommu");
if (ret)
sysfs_remove_link_from_group(&dev->kobj, "devices",
dev_name(link));
return ret;
}
void iommu_device_unlink(struct device *dev, struct device *link)
{
if (!dev || IS_ERR(dev))
return;
sysfs_remove_link(&link->kobj, "iommu");
sysfs_remove_link_from_group(&dev->kobj, "devices", dev_name(link));
}
...@@ -29,12 +29,17 @@ ...@@ -29,12 +29,17 @@
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/pci.h>
#include <trace/events/iommu.h> #include <trace/events/iommu.h>
static struct kset *iommu_group_kset; static struct kset *iommu_group_kset;
static struct ida iommu_group_ida; static struct ida iommu_group_ida;
static struct mutex iommu_group_mutex; static struct mutex iommu_group_mutex;
struct iommu_callback_data {
const struct iommu_ops *ops;
};
struct iommu_group { struct iommu_group {
struct kobject kobj; struct kobject kobj;
struct kobject *devices_kobj; struct kobject *devices_kobj;
...@@ -514,9 +519,191 @@ int iommu_group_id(struct iommu_group *group) ...@@ -514,9 +519,191 @@ int iommu_group_id(struct iommu_group *group)
} }
EXPORT_SYMBOL_GPL(iommu_group_id); EXPORT_SYMBOL_GPL(iommu_group_id);
/*
* To consider a PCI device isolated, we require ACS to support Source
* Validation, Request Redirection, Completer Redirection, and Upstream
* Forwarding. This effectively means that devices cannot spoof their
* requester ID, requests and completions cannot be redirected, and all
* transactions are forwarded upstream, even as it passes through a
* bridge where the target device is downstream.
*/
#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
struct group_for_pci_data {
struct pci_dev *pdev;
struct iommu_group *group;
};
/*
* DMA alias iterator callback, return the last seen device. Stop and return
* the IOMMU group if we find one along the way.
*/
static int get_pci_alias_or_group(struct pci_dev *pdev, u16 alias, void *opaque)
{
struct group_for_pci_data *data = opaque;
data->pdev = pdev;
data->group = iommu_group_get(&pdev->dev);
return data->group != NULL;
}
/*
* Use standard PCI bus topology, isolation features, and DMA alias quirks
* to find or create an IOMMU group for a device.
*/
static struct iommu_group *iommu_group_get_for_pci_dev(struct pci_dev *pdev)
{
struct group_for_pci_data data;
struct pci_bus *bus;
struct iommu_group *group = NULL;
struct pci_dev *tmp;
/*
* Find the upstream DMA alias for the device. A device must not
* be aliased due to topology in order to have its own IOMMU group.
* If we find an alias along the way that already belongs to a
* group, use it.
*/
if (pci_for_each_dma_alias(pdev, get_pci_alias_or_group, &data))
return data.group;
pdev = data.pdev;
/*
* Continue upstream from the point of minimum IOMMU granularity
* due to aliases to the point where devices are protected from
* peer-to-peer DMA by PCI ACS. Again, if we find an existing
* group, use it.
*/
for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
if (!bus->self)
continue;
if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
break;
pdev = bus->self;
group = iommu_group_get(&pdev->dev);
if (group)
return group;
}
/*
* Next we need to consider DMA alias quirks. If one device aliases
* to another, they should be grouped together. It's theoretically
* possible that aliases could create chains of devices where each
* device aliases another device. If we then factor in multifunction
* ACS grouping requirements, each alias could incorporate a new slot
* with multiple functions, each with aliases. This is all extremely
* unlikely as DMA alias quirks are typically only used for PCIe
* devices where we usually have a single slot per bus. Furthermore,
* the alias quirk is usually to another function within the slot
* (and ACS multifunction is not supported) or to a different slot
* that doesn't physically exist. The likely scenario is therefore
* that everything on the bus gets grouped together. To reduce the
* problem space, share the IOMMU group for all devices on the bus
* if a DMA alias quirk is present on the bus.
*/
tmp = NULL;
for_each_pci_dev(tmp) {
if (tmp->bus != pdev->bus ||
!(tmp->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN))
continue;
pci_dev_put(tmp);
tmp = NULL;
/* We have an alias quirk, search for an existing group */
for_each_pci_dev(tmp) {
struct iommu_group *group_tmp;
if (tmp->bus != pdev->bus)
continue;
group_tmp = iommu_group_get(&tmp->dev);
if (!group) {
group = group_tmp;
continue;
}
if (group_tmp) {
WARN_ON(group != group_tmp);
iommu_group_put(group_tmp);
}
}
return group ? group : iommu_group_alloc();
}
/*
* Non-multifunction devices or multifunction devices supporting
* ACS get their own group.
*/
if (!pdev->multifunction || pci_acs_enabled(pdev, REQ_ACS_FLAGS))
return iommu_group_alloc();
/*
* Multifunction devices not supporting ACS share a group with other
* similar devices in the same slot.
*/
tmp = NULL;
for_each_pci_dev(tmp) {
if (tmp == pdev || tmp->bus != pdev->bus ||
PCI_SLOT(tmp->devfn) != PCI_SLOT(pdev->devfn) ||
pci_acs_enabled(tmp, REQ_ACS_FLAGS))
continue;
group = iommu_group_get(&tmp->dev);
if (group) {
pci_dev_put(tmp);
return group;
}
}
/* No shared group found, allocate new */
return iommu_group_alloc();
}
/**
* iommu_group_get_for_dev - Find or create the IOMMU group for a device
* @dev: target device
*
* This function is intended to be called by IOMMU drivers and extended to
* support common, bus-defined algorithms when determining or creating the
* IOMMU group for a device. On success, the caller will hold a reference
* to the returned IOMMU group, which will already include the provided
* device. The reference should be released with iommu_group_put().
*/
struct iommu_group *iommu_group_get_for_dev(struct device *dev)
{
struct iommu_group *group = ERR_PTR(-EIO);
int ret;
group = iommu_group_get(dev);
if (group)
return group;
if (dev_is_pci(dev))
group = iommu_group_get_for_pci_dev(to_pci_dev(dev));
if (IS_ERR(group))
return group;
ret = iommu_group_add_device(group, dev);
if (ret) {
iommu_group_put(group);
return ERR_PTR(ret);
}
return group;
}
static int add_iommu_group(struct device *dev, void *data) static int add_iommu_group(struct device *dev, void *data)
{ {
struct iommu_ops *ops = data; struct iommu_callback_data *cb = data;
const struct iommu_ops *ops = cb->ops;
if (!ops->add_device) if (!ops->add_device)
return -ENODEV; return -ENODEV;
...@@ -532,7 +719,7 @@ static int iommu_bus_notifier(struct notifier_block *nb, ...@@ -532,7 +719,7 @@ static int iommu_bus_notifier(struct notifier_block *nb,
unsigned long action, void *data) unsigned long action, void *data)
{ {
struct device *dev = data; struct device *dev = data;
struct iommu_ops *ops = dev->bus->iommu_ops; const struct iommu_ops *ops = dev->bus->iommu_ops;
struct iommu_group *group; struct iommu_group *group;
unsigned long group_action = 0; unsigned long group_action = 0;
...@@ -585,10 +772,14 @@ static struct notifier_block iommu_bus_nb = { ...@@ -585,10 +772,14 @@ static struct notifier_block iommu_bus_nb = {
.notifier_call = iommu_bus_notifier, .notifier_call = iommu_bus_notifier,
}; };
static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops) static void iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops)
{ {
struct iommu_callback_data cb = {
.ops = ops,
};
bus_register_notifier(bus, &iommu_bus_nb); bus_register_notifier(bus, &iommu_bus_nb);
bus_for_each_dev(bus, NULL, ops, add_iommu_group); bus_for_each_dev(bus, NULL, &cb, add_iommu_group);
} }
/** /**
...@@ -604,7 +795,7 @@ static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops) ...@@ -604,7 +795,7 @@ static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops)
* is set up. With this function the iommu-driver can set the iommu-ops * is set up. With this function the iommu-driver can set the iommu-ops
* afterwards. * afterwards.
*/ */
int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops) int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops)
{ {
if (bus->iommu_ops != NULL) if (bus->iommu_ops != NULL)
return -EBUSY; return -EBUSY;
......
...@@ -1120,7 +1120,7 @@ static void ipmmu_remove_device(struct device *dev) ...@@ -1120,7 +1120,7 @@ static void ipmmu_remove_device(struct device *dev)
dev->archdata.iommu = NULL; dev->archdata.iommu = NULL;
} }
static struct iommu_ops ipmmu_ops = { static const struct iommu_ops ipmmu_ops = {
.domain_init = ipmmu_domain_init, .domain_init = ipmmu_domain_init,
.domain_destroy = ipmmu_domain_destroy, .domain_destroy = ipmmu_domain_destroy,
.attach_dev = ipmmu_attach_device, .attach_dev = ipmmu_attach_device,
......
...@@ -674,7 +674,7 @@ irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id) ...@@ -674,7 +674,7 @@ irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id)
return 0; return 0;
} }
static struct iommu_ops msm_iommu_ops = { static const struct iommu_ops msm_iommu_ops = {
.domain_init = msm_iommu_domain_init, .domain_init = msm_iommu_domain_init,
.domain_destroy = msm_iommu_domain_destroy, .domain_destroy = msm_iommu_domain_destroy,
.attach_dev = msm_iommu_attach_dev, .attach_dev = msm_iommu_attach_dev,
......
...@@ -1291,7 +1291,7 @@ static void omap_iommu_remove_device(struct device *dev) ...@@ -1291,7 +1291,7 @@ static void omap_iommu_remove_device(struct device *dev)
kfree(arch_data); kfree(arch_data);
} }
static struct iommu_ops omap_iommu_ops = { static const struct iommu_ops omap_iommu_ops = {
.domain_init = omap_iommu_domain_init, .domain_init = omap_iommu_domain_init,
.domain_destroy = omap_iommu_domain_destroy, .domain_destroy = omap_iommu_domain_destroy,
.attach_dev = omap_iommu_attach_dev, .attach_dev = omap_iommu_attach_dev,
......
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (C) 2013 Red Hat, Inc.
* Copyright (C) 2013 Freescale Semiconductor, Inc.
*
*/
#ifndef __IOMMU_PCI_H
#define __IOMMU_PCI_H
/* Helper function for swapping pci device reference */
static inline void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
{
pci_dev_put(*from);
*from = to;
}
#endif /* __IOMMU_PCI_H */
...@@ -354,7 +354,7 @@ static int shmobile_iommu_add_device(struct device *dev) ...@@ -354,7 +354,7 @@ static int shmobile_iommu_add_device(struct device *dev)
return 0; return 0;
} }
static struct iommu_ops shmobile_iommu_ops = { static const struct iommu_ops shmobile_iommu_ops = {
.domain_init = shmobile_iommu_domain_init, .domain_init = shmobile_iommu_domain_init,
.domain_destroy = shmobile_iommu_domain_destroy, .domain_destroy = shmobile_iommu_domain_destroy,
.attach_dev = shmobile_iommu_attach_device, .attach_dev = shmobile_iommu_attach_device,
......
...@@ -309,7 +309,7 @@ static int gart_iommu_domain_has_cap(struct iommu_domain *domain, ...@@ -309,7 +309,7 @@ static int gart_iommu_domain_has_cap(struct iommu_domain *domain,
return 0; return 0;
} }
static struct iommu_ops gart_iommu_ops = { static const struct iommu_ops gart_iommu_ops = {
.domain_init = gart_iommu_domain_init, .domain_init = gart_iommu_domain_init,
.domain_destroy = gart_iommu_domain_destroy, .domain_destroy = gart_iommu_domain_destroy,
.attach_dev = gart_iommu_attach_dev, .attach_dev = gart_iommu_attach_dev,
......
...@@ -947,7 +947,7 @@ static void smmu_iommu_domain_destroy(struct iommu_domain *domain) ...@@ -947,7 +947,7 @@ static void smmu_iommu_domain_destroy(struct iommu_domain *domain)
dev_dbg(smmu->dev, "smmu_as@%p\n", as); dev_dbg(smmu->dev, "smmu_as@%p\n", as);
} }
static struct iommu_ops smmu_iommu_ops = { static const struct iommu_ops smmu_iommu_ops = {
.domain_init = smmu_iommu_domain_init, .domain_init = smmu_iommu_domain_init,
.domain_destroy = smmu_iommu_domain_destroy, .domain_destroy = smmu_iommu_domain_destroy,
.attach_dev = smmu_iommu_attach_dev, .attach_dev = smmu_iommu_attach_dev,
......
...@@ -124,7 +124,7 @@ struct bus_type { ...@@ -124,7 +124,7 @@ struct bus_type {
const struct dev_pm_ops *pm; const struct dev_pm_ops *pm;
struct iommu_ops *iommu_ops; const struct iommu_ops *iommu_ops;
struct subsys_private *p; struct subsys_private *p;
struct lock_class_key lock_key; struct lock_class_key lock_key;
......
...@@ -336,6 +336,7 @@ struct intel_iommu { ...@@ -336,6 +336,7 @@ struct intel_iommu {
#ifdef CONFIG_IRQ_REMAP #ifdef CONFIG_IRQ_REMAP
struct ir_table *ir_table; /* Interrupt remapping info */ struct ir_table *ir_table; /* Interrupt remapping info */
#endif #endif
struct device *iommu_dev; /* IOMMU-sysfs device */
int node; int node;
}; };
...@@ -365,4 +366,6 @@ extern int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); ...@@ -365,4 +366,6 @@ extern int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu);
extern int dmar_ir_support(void); extern int dmar_ir_support(void);
extern const struct attribute_group *intel_iommu_groups[];
#endif #endif
...@@ -50,7 +50,7 @@ struct iommu_domain_geometry { ...@@ -50,7 +50,7 @@ struct iommu_domain_geometry {
}; };
struct iommu_domain { struct iommu_domain {
struct iommu_ops *ops; const struct iommu_ops *ops;
void *priv; void *priv;
iommu_fault_handler_t handler; iommu_fault_handler_t handler;
void *handler_token; void *handler_token;
...@@ -140,7 +140,7 @@ struct iommu_ops { ...@@ -140,7 +140,7 @@ struct iommu_ops {
#define IOMMU_GROUP_NOTIFY_UNBIND_DRIVER 5 /* Pre Driver unbind */ #define IOMMU_GROUP_NOTIFY_UNBIND_DRIVER 5 /* Pre Driver unbind */
#define IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER 6 /* Post Driver unbind */ #define IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER 6 /* Post Driver unbind */
extern int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops); extern int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops);
extern bool iommu_present(struct bus_type *bus); extern bool iommu_present(struct bus_type *bus);
extern struct iommu_domain *iommu_domain_alloc(struct bus_type *bus); extern struct iommu_domain *iommu_domain_alloc(struct bus_type *bus);
extern struct iommu_group *iommu_group_get_by_id(int id); extern struct iommu_group *iommu_group_get_by_id(int id);
...@@ -181,11 +181,18 @@ extern int iommu_group_register_notifier(struct iommu_group *group, ...@@ -181,11 +181,18 @@ extern int iommu_group_register_notifier(struct iommu_group *group,
extern int iommu_group_unregister_notifier(struct iommu_group *group, extern int iommu_group_unregister_notifier(struct iommu_group *group,
struct notifier_block *nb); struct notifier_block *nb);
extern int iommu_group_id(struct iommu_group *group); extern int iommu_group_id(struct iommu_group *group);
extern struct iommu_group *iommu_group_get_for_dev(struct device *dev);
extern int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr, extern int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr,
void *data); void *data);
extern int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr, extern int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr,
void *data); void *data);
struct device *iommu_device_create(struct device *parent, void *drvdata,
const struct attribute_group **groups,
const char *fmt, ...);
void iommu_device_destroy(struct device *dev);
int iommu_device_link(struct device *dev, struct device *link);
void iommu_device_unlink(struct device *dev, struct device *link);
/* Window handling function prototypes */ /* Window handling function prototypes */
extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr,
...@@ -396,6 +403,27 @@ static inline int iommu_domain_set_attr(struct iommu_domain *domain, ...@@ -396,6 +403,27 @@ static inline int iommu_domain_set_attr(struct iommu_domain *domain,
return -EINVAL; return -EINVAL;
} }
static inline struct device *iommu_device_create(struct device *parent,
void *drvdata,
const struct attribute_group **groups,
const char *fmt, ...)
{
return ERR_PTR(-ENODEV);
}
static inline void iommu_device_destroy(struct device *dev)
{
}
static inline int iommu_device_link(struct device *dev, struct device *link)
{
return -EINVAL;
}
static inline void iommu_device_unlink(struct device *dev, struct device *link)
{
}
#endif /* CONFIG_IOMMU_API */ #endif /* CONFIG_IOMMU_API */
#endif /* __LINUX_IOMMU_H */ #endif /* __LINUX_IOMMU_H */
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