Merge branch 'core' into x86/vt-d
Conflicts: drivers/iommu/intel-iommu.c
This commit is contained in:
commit
cbb24a25a8
|
@ -0,0 +1,17 @@
|
|||
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.
|
|
@ -0,0 +1,14 @@
|
|||
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
|
|
@ -0,0 +1,32 @@
|
|||
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
|
|
@ -1,5 +1,6 @@
|
|||
obj-$(CONFIG_IOMMU_API) += iommu.o
|
||||
obj-$(CONFIG_IOMMU_API) += iommu-traces.o
|
||||
obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
|
||||
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
|
||||
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
|
||||
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
|
||||
|
|
|
@ -46,7 +46,6 @@
|
|||
#include "amd_iommu_proto.h"
|
||||
#include "amd_iommu_types.h"
|
||||
#include "irq_remapping.h"
|
||||
#include "pci.h"
|
||||
|
||||
#define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28))
|
||||
|
||||
|
@ -81,7 +80,7 @@ LIST_HEAD(hpet_map);
|
|||
*/
|
||||
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);
|
||||
int amd_iommu_max_glx_val = -1;
|
||||
|
@ -133,9 +132,6 @@ static void free_dev_data(struct iommu_dev_data *dev_data)
|
|||
list_del(&dev_data->dev_data_list);
|
||||
spin_unlock_irqrestore(&dev_data_list_lock, flags);
|
||||
|
||||
if (dev_data->group)
|
||||
iommu_group_put(dev_data->group);
|
||||
|
||||
kfree(dev_data);
|
||||
}
|
||||
|
||||
|
@ -264,167 +260,79 @@ static bool check_device(struct device *dev)
|
|||
return true;
|
||||
}
|
||||
|
||||
static struct pci_bus *find_hosted_bus(struct pci_bus *bus)
|
||||
{
|
||||
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);
|
||||
int ret;
|
||||
|
||||
if (!group) {
|
||||
group = iommu_group_alloc();
|
||||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
|
||||
WARN_ON(&pdev->dev != dev);
|
||||
}
|
||||
|
||||
ret = iommu_group_add_device(group, dev);
|
||||
iommu_group_put(group);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int use_dev_data_iommu_group(struct iommu_dev_data *dev_data,
|
||||
struct device *dev)
|
||||
{
|
||||
if (!dev_data->group) {
|
||||
struct iommu_group *group = iommu_group_alloc();
|
||||
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)
|
||||
{
|
||||
struct iommu_dev_data *dev_data;
|
||||
struct iommu_group *group;
|
||||
struct pci_dev *dma_pdev;
|
||||
int ret;
|
||||
|
||||
group = iommu_group_get(dev);
|
||||
if (group) {
|
||||
iommu_group_put(group);
|
||||
return 0;
|
||||
}
|
||||
group = iommu_group_get_for_dev(dev);
|
||||
|
||||
dev_data = find_dev_data(get_device_id(dev));
|
||||
if (!dev_data)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
|
||||
if (dev_data->alias_data) {
|
||||
u16 alias;
|
||||
struct pci_bus *bus;
|
||||
iommu_group_put(group);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dev_data->alias_data->group)
|
||||
goto use_group;
|
||||
static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
|
||||
{
|
||||
*(u16 *)data = alias;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the alias device exists, it's effectively just a first
|
||||
* level quirk for finding the DMA source.
|
||||
*/
|
||||
alias = amd_iommu_alias_table[dev_data->devid];
|
||||
dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff);
|
||||
if (dma_pdev) {
|
||||
dma_pdev = get_isolation_root(dma_pdev);
|
||||
goto use_pdev;
|
||||
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));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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));
|
||||
if (dma_pdev != bus->self || (dma_pdev->multifunction &&
|
||||
!pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)))
|
||||
goto use_pdev;
|
||||
|
||||
pci_dev_put(dma_pdev);
|
||||
goto use_group;
|
||||
return pci_alias;
|
||||
}
|
||||
|
||||
dma_pdev = get_isolation_root(pci_dev_get(to_pci_dev(dev)));
|
||||
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);
|
||||
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)
|
||||
|
@ -441,7 +349,8 @@ static int iommu_init_device(struct device *dev)
|
|||
if (!dev_data)
|
||||
return -ENOMEM;
|
||||
|
||||
alias = amd_iommu_alias_table[dev_data->devid];
|
||||
alias = get_alias(dev);
|
||||
|
||||
if (alias != dev_data->devid) {
|
||||
struct iommu_dev_data *alias_data;
|
||||
|
||||
|
@ -470,6 +379,9 @@ static int iommu_init_device(struct device *dev)
|
|||
|
||||
dev->archdata.iommu = dev_data;
|
||||
|
||||
iommu_device_link(amd_iommu_rlookup_table[dev_data->devid]->iommu_dev,
|
||||
dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -489,12 +401,22 @@ static void iommu_ignore_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);
|
||||
|
||||
/* 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
|
||||
* and reuse it when the device is re-plugged - not doing so would
|
||||
* introduce a ton of races.
|
||||
* We keep dev_data around for unplugged devices and reuse it when the
|
||||
* device is re-plugged - not doing so would introduce a ton of races.
|
||||
*/
|
||||
}
|
||||
|
||||
|
@ -3473,7 +3395,7 @@ static int amd_iommu_domain_has_cap(struct iommu_domain *domain,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct iommu_ops amd_iommu_ops = {
|
||||
static const struct iommu_ops amd_iommu_ops = {
|
||||
.domain_init = amd_iommu_domain_init,
|
||||
.domain_destroy = amd_iommu_domain_destroy,
|
||||
.attach_dev = amd_iommu_attach_device,
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/msi.h>
|
||||
#include <linux/amd-iommu.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <asm/pci-direct.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/gart.h>
|
||||
|
@ -1197,6 +1198,39 @@ static void init_iommu_perf_ctr(struct amd_iommu *iommu)
|
|||
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)
|
||||
{
|
||||
|
@ -1297,6 +1331,10 @@ static int iommu_init_pci(struct amd_iommu *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);
|
||||
}
|
||||
|
||||
|
|
|
@ -432,7 +432,6 @@ struct iommu_dev_data {
|
|||
struct iommu_dev_data *alias_data;/* The alias dev_data */
|
||||
struct protection_domain *domain; /* Domain the device is bound to */
|
||||
atomic_t bind; /* Domain attach reference count */
|
||||
struct iommu_group *group; /* IOMMU group for virtual aliases */
|
||||
u16 devid; /* PCI Device ID */
|
||||
bool iommu_v2; /* Device can make use of IOMMUv2 */
|
||||
bool passthrough; /* Default for device is pt_domain */
|
||||
|
@ -578,6 +577,9 @@ struct amd_iommu {
|
|||
/* default dma_ops domain for that IOMMU */
|
||||
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
|
||||
* need to stash them
|
||||
|
|
|
@ -1609,7 +1609,7 @@ static void arm_smmu_remove_device(struct 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_destroy = arm_smmu_domain_destroy,
|
||||
.attach_dev = arm_smmu_attach_dev,
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <linux/tboot.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <asm/irq_remapping.h>
|
||||
#include <asm/iommu_table.h>
|
||||
|
||||
|
@ -980,6 +981,12 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
|
|||
raw_spin_lock_init(&iommu->register_lock);
|
||||
|
||||
drhd->iommu = iommu;
|
||||
|
||||
if (intel_iommu_enabled)
|
||||
iommu->iommu_dev = iommu_device_create(NULL, iommu,
|
||||
intel_iommu_groups,
|
||||
iommu->name);
|
||||
|
||||
return 0;
|
||||
|
||||
err_unmap:
|
||||
|
@ -991,6 +998,8 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
|
|||
|
||||
static void free_iommu(struct intel_iommu *iommu)
|
||||
{
|
||||
iommu_device_destroy(iommu->iommu_dev);
|
||||
|
||||
if (iommu->irq) {
|
||||
free_irq(iommu->irq, iommu);
|
||||
irq_set_handler_data(iommu->irq, NULL);
|
||||
|
|
|
@ -1170,7 +1170,7 @@ static void exynos_iommu_remove_device(struct 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_destroy = exynos_iommu_domain_destroy,
|
||||
.attach_dev = exynos_iommu_attach_device,
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include <sysdev/fsl_pci.h>
|
||||
|
||||
#include "fsl_pamu_domain.h"
|
||||
#include "pci.h"
|
||||
|
||||
/*
|
||||
* Global spinlock that needs to be held while
|
||||
|
@ -892,8 +891,6 @@ static int fsl_pamu_get_domain_attr(struct iommu_domain *domain,
|
|||
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)
|
||||
{
|
||||
struct iommu_group *group;
|
||||
|
@ -950,74 +947,13 @@ static struct iommu_group *get_pci_device_group(struct pci_dev *pdev)
|
|||
struct pci_controller *pci_ctl;
|
||||
bool pci_endpt_partioning;
|
||||
struct iommu_group *group = NULL;
|
||||
struct pci_dev *bridge, *dma_pdev = NULL;
|
||||
|
||||
pci_ctl = pci_bus_to_host(pdev->bus);
|
||||
pci_endpt_partioning = check_pci_ctl_endpt_part(pci_ctl);
|
||||
/* We can partition PCIe devices so assign device group to the device */
|
||||
if (pci_endpt_partioning) {
|
||||
bridge = pci_find_upstream_pcie_bridge(pdev);
|
||||
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);
|
||||
group = iommu_group_get_for_dev(&pdev->dev);
|
||||
|
||||
/* 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
|
||||
* free the controller device iommu_group.
|
||||
|
@ -1140,7 +1076,7 @@ static u32 fsl_pamu_get_windows(struct iommu_domain *domain)
|
|||
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_destroy = fsl_pamu_domain_destroy,
|
||||
.attach_dev = fsl_pamu_attach_device,
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
#include <asm/iommu.h>
|
||||
|
||||
#include "irq_remapping.h"
|
||||
#include "pci.h"
|
||||
|
||||
#define ROOT_SIZE VTD_PAGE_SIZE
|
||||
#define CONTEXT_SIZE VTD_PAGE_SIZE
|
||||
|
@ -451,7 +450,7 @@ EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped);
|
|||
static DEFINE_SPINLOCK(device_domain_lock);
|
||||
static LIST_HEAD(device_domain_list);
|
||||
|
||||
static struct iommu_ops intel_iommu_ops;
|
||||
static const struct iommu_ops intel_iommu_ops;
|
||||
|
||||
static int __init intel_iommu_setup(char *str)
|
||||
{
|
||||
|
@ -1840,54 +1839,56 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct domain_context_mapping_data {
|
||||
struct dmar_domain *domain;
|
||||
struct intel_iommu *iommu;
|
||||
int translation;
|
||||
};
|
||||
|
||||
static int domain_context_mapping_cb(struct pci_dev *pdev,
|
||||
u16 alias, void *opaque)
|
||||
{
|
||||
struct domain_context_mapping_data *data = opaque;
|
||||
|
||||
return domain_context_mapping_one(data->domain, data->iommu,
|
||||
PCI_BUS_NUM(alias), alias & 0xff,
|
||||
data->translation);
|
||||
}
|
||||
|
||||
static int
|
||||
domain_context_mapping(struct dmar_domain *domain, struct device *dev,
|
||||
int translation)
|
||||
{
|
||||
int ret;
|
||||
struct pci_dev *pdev, *tmp, *parent;
|
||||
struct intel_iommu *iommu;
|
||||
u8 bus, devfn;
|
||||
struct domain_context_mapping_data data;
|
||||
|
||||
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||
if (!iommu)
|
||||
return -ENODEV;
|
||||
|
||||
ret = domain_context_mapping_one(domain, iommu, bus, devfn,
|
||||
translation);
|
||||
if (ret || !dev_is_pci(dev))
|
||||
return ret;
|
||||
|
||||
/* dependent device mapping */
|
||||
pdev = to_pci_dev(dev);
|
||||
tmp = pci_find_upstream_pcie_bridge(pdev);
|
||||
if (!tmp)
|
||||
return 0;
|
||||
/* Secondary interface's bus number and devfn 0 */
|
||||
parent = pdev->bus->self;
|
||||
while (parent != tmp) {
|
||||
ret = domain_context_mapping_one(domain, iommu,
|
||||
parent->bus->number,
|
||||
parent->devfn, translation);
|
||||
if (ret)
|
||||
return ret;
|
||||
parent = parent->bus->self;
|
||||
}
|
||||
if (pci_is_pcie(tmp)) /* this is a PCIe-to-PCI bridge */
|
||||
return domain_context_mapping_one(domain, iommu,
|
||||
tmp->subordinate->number, 0,
|
||||
translation);
|
||||
else /* this is a legacy PCI bridge */
|
||||
return domain_context_mapping_one(domain, iommu,
|
||||
tmp->bus->number,
|
||||
tmp->devfn,
|
||||
if (!dev_is_pci(dev))
|
||||
return domain_context_mapping_one(domain, iommu, bus, devfn,
|
||||
translation);
|
||||
|
||||
data.domain = domain;
|
||||
data.iommu = iommu;
|
||||
data.translation = translation;
|
||||
|
||||
return pci_for_each_dma_alias(to_pci_dev(dev),
|
||||
&domain_context_mapping_cb, &data);
|
||||
}
|
||||
|
||||
static int domain_context_mapped_cb(struct pci_dev *pdev,
|
||||
u16 alias, void *opaque)
|
||||
{
|
||||
struct intel_iommu *iommu = opaque;
|
||||
|
||||
return !device_context_mapped(iommu, PCI_BUS_NUM(alias), alias & 0xff);
|
||||
}
|
||||
|
||||
static int domain_context_mapped(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct pci_dev *pdev, *tmp, *parent;
|
||||
struct intel_iommu *iommu;
|
||||
u8 bus, devfn;
|
||||
|
||||
|
@ -1895,30 +1896,11 @@ static int domain_context_mapped(struct device *dev)
|
|||
if (!iommu)
|
||||
return -ENODEV;
|
||||
|
||||
ret = device_context_mapped(iommu, bus, devfn);
|
||||
if (!ret || !dev_is_pci(dev))
|
||||
return ret;
|
||||
if (!dev_is_pci(dev))
|
||||
return device_context_mapped(iommu, bus, devfn);
|
||||
|
||||
/* dependent device mapping */
|
||||
pdev = to_pci_dev(dev);
|
||||
tmp = pci_find_upstream_pcie_bridge(pdev);
|
||||
if (!tmp)
|
||||
return ret;
|
||||
/* Secondary interface's bus number and devfn 0 */
|
||||
parent = pdev->bus->self;
|
||||
while (parent != tmp) {
|
||||
ret = device_context_mapped(iommu, parent->bus->number,
|
||||
parent->devfn);
|
||||
if (!ret)
|
||||
return ret;
|
||||
parent = parent->bus->self;
|
||||
}
|
||||
if (pci_is_pcie(tmp))
|
||||
return device_context_mapped(iommu, tmp->subordinate->number,
|
||||
0);
|
||||
else
|
||||
return device_context_mapped(iommu, tmp->bus->number,
|
||||
tmp->devfn);
|
||||
return !pci_for_each_dma_alias(to_pci_dev(dev),
|
||||
domain_context_mapped_cb, iommu);
|
||||
}
|
||||
|
||||
/* Returns a number of VTD pages, but aligned to MM page size */
|
||||
|
@ -2205,80 +2187,86 @@ static struct dmar_domain *dmar_insert_dev_info(struct intel_iommu *iommu,
|
|||
return domain;
|
||||
}
|
||||
|
||||
static int get_last_alias(struct pci_dev *pdev, u16 alias, void *opaque)
|
||||
{
|
||||
*(u16 *)opaque = alias;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* domain is initialized */
|
||||
static struct dmar_domain *get_domain_for_dev(struct device *dev, int gaw)
|
||||
{
|
||||
struct dmar_domain *domain, *free = NULL;
|
||||
struct intel_iommu *iommu = NULL;
|
||||
struct dmar_domain *domain, *tmp;
|
||||
struct intel_iommu *iommu;
|
||||
struct device_domain_info *info;
|
||||
struct pci_dev *dev_tmp = NULL;
|
||||
u16 dma_alias;
|
||||
unsigned long flags;
|
||||
u8 bus, devfn;
|
||||
u8 bridge_bus = 0, bridge_devfn = 0;
|
||||
|
||||
domain = find_domain(dev);
|
||||
if (domain)
|
||||
return domain;
|
||||
|
||||
if (dev_is_pci(dev)) {
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
u16 segment;
|
||||
|
||||
segment = pci_domain_nr(pdev->bus);
|
||||
dev_tmp = pci_find_upstream_pcie_bridge(pdev);
|
||||
if (dev_tmp) {
|
||||
if (pci_is_pcie(dev_tmp)) {
|
||||
bridge_bus = dev_tmp->subordinate->number;
|
||||
bridge_devfn = 0;
|
||||
} else {
|
||||
bridge_bus = dev_tmp->bus->number;
|
||||
bridge_devfn = dev_tmp->devfn;
|
||||
}
|
||||
spin_lock_irqsave(&device_domain_lock, flags);
|
||||
info = dmar_search_domain_by_dev_info(segment,
|
||||
bridge_bus,
|
||||
bridge_devfn);
|
||||
if (info) {
|
||||
iommu = info->iommu;
|
||||
domain = info->domain;
|
||||
}
|
||||
spin_unlock_irqrestore(&device_domain_lock, flags);
|
||||
/* pcie-pci bridge already has a domain, uses it */
|
||||
if (info)
|
||||
goto found_domain;
|
||||
}
|
||||
}
|
||||
|
||||
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||
if (!iommu)
|
||||
goto error;
|
||||
return NULL;
|
||||
|
||||
if (dev_is_pci(dev)) {
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
||||
pci_for_each_dma_alias(pdev, get_last_alias, &dma_alias);
|
||||
|
||||
spin_lock_irqsave(&device_domain_lock, flags);
|
||||
info = dmar_search_domain_by_dev_info(pci_domain_nr(pdev->bus),
|
||||
PCI_BUS_NUM(dma_alias),
|
||||
dma_alias & 0xff);
|
||||
if (info) {
|
||||
iommu = info->iommu;
|
||||
domain = info->domain;
|
||||
}
|
||||
spin_unlock_irqrestore(&device_domain_lock, flags);
|
||||
|
||||
/* DMA alias already has a domain, uses it */
|
||||
if (info)
|
||||
goto found_domain;
|
||||
}
|
||||
|
||||
/* Allocate and initialize new domain for the device */
|
||||
domain = alloc_domain(false);
|
||||
if (!domain)
|
||||
goto error;
|
||||
return NULL;
|
||||
|
||||
if (iommu_attach_domain(domain, iommu)) {
|
||||
free_domain_mem(domain);
|
||||
domain = NULL;
|
||||
goto error;
|
||||
return NULL;
|
||||
}
|
||||
free = domain;
|
||||
if (domain_init(domain, gaw))
|
||||
goto error;
|
||||
|
||||
/* register pcie-to-pci device */
|
||||
if (dev_tmp) {
|
||||
domain = dmar_insert_dev_info(iommu, bridge_bus, bridge_devfn,
|
||||
NULL, domain);
|
||||
if (domain_init(domain, gaw)) {
|
||||
domain_exit(domain);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* register PCI DMA alias device */
|
||||
if (dev_is_pci(dev)) {
|
||||
tmp = dmar_insert_dev_info(iommu, PCI_BUS_NUM(dma_alias),
|
||||
dma_alias & 0xff, NULL, domain);
|
||||
|
||||
if (!tmp || tmp != domain) {
|
||||
domain_exit(domain);
|
||||
domain = tmp;
|
||||
}
|
||||
|
||||
if (!domain)
|
||||
goto error;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
found_domain:
|
||||
domain = dmar_insert_dev_info(iommu, bus, devfn, dev, domain);
|
||||
error:
|
||||
if (free != domain)
|
||||
domain_exit(free);
|
||||
tmp = dmar_insert_dev_info(iommu, bus, devfn, dev, domain);
|
||||
|
||||
if (!tmp || tmp != domain) {
|
||||
domain_exit(domain);
|
||||
domain = tmp;
|
||||
}
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
@ -3953,6 +3941,63 @@ static struct notifier_block intel_iommu_memory_nb = {
|
|||
.priority = 0
|
||||
};
|
||||
|
||||
|
||||
static ssize_t intel_iommu_show_version(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct intel_iommu *iommu = dev_get_drvdata(dev);
|
||||
u32 ver = readl(iommu->reg + DMAR_VER_REG);
|
||||
return sprintf(buf, "%d:%d\n",
|
||||
DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver));
|
||||
}
|
||||
static DEVICE_ATTR(version, S_IRUGO, intel_iommu_show_version, NULL);
|
||||
|
||||
static ssize_t intel_iommu_show_address(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct intel_iommu *iommu = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%llx\n", iommu->reg_phys);
|
||||
}
|
||||
static DEVICE_ATTR(address, S_IRUGO, intel_iommu_show_address, NULL);
|
||||
|
||||
static ssize_t intel_iommu_show_cap(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct intel_iommu *iommu = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%llx\n", iommu->cap);
|
||||
}
|
||||
static DEVICE_ATTR(cap, S_IRUGO, intel_iommu_show_cap, NULL);
|
||||
|
||||
static ssize_t intel_iommu_show_ecap(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct intel_iommu *iommu = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%llx\n", iommu->ecap);
|
||||
}
|
||||
static DEVICE_ATTR(ecap, S_IRUGO, intel_iommu_show_ecap, NULL);
|
||||
|
||||
static struct attribute *intel_iommu_attrs[] = {
|
||||
&dev_attr_version.attr,
|
||||
&dev_attr_address.attr,
|
||||
&dev_attr_cap.attr,
|
||||
&dev_attr_ecap.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group intel_iommu_group = {
|
||||
.name = "intel-iommu",
|
||||
.attrs = intel_iommu_attrs,
|
||||
};
|
||||
|
||||
const struct attribute_group *intel_iommu_groups[] = {
|
||||
&intel_iommu_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
int __init intel_iommu_init(void)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
|
@ -4024,6 +4069,11 @@ int __init intel_iommu_init(void)
|
|||
|
||||
init_iommu_pm_ops();
|
||||
|
||||
for_each_active_iommu(iommu, drhd)
|
||||
iommu->iommu_dev = iommu_device_create(NULL, iommu,
|
||||
intel_iommu_groups,
|
||||
iommu->name);
|
||||
|
||||
bus_set_iommu(&pci_bus_type, &intel_iommu_ops);
|
||||
bus_register_notifier(&pci_bus_type, &device_nb);
|
||||
if (si_domain && !hw_pass_through)
|
||||
|
@ -4042,33 +4092,27 @@ out_free_dmar:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int iommu_detach_dev_cb(struct pci_dev *pdev, u16 alias, void *opaque)
|
||||
{
|
||||
struct intel_iommu *iommu = opaque;
|
||||
|
||||
iommu_detach_dev(iommu, PCI_BUS_NUM(alias), alias & 0xff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NB - intel-iommu lacks any sort of reference counting for the users of
|
||||
* dependent devices. If multiple endpoints have intersecting dependent
|
||||
* devices, unbinding the driver from any one of them will possibly leave
|
||||
* the others unable to operate.
|
||||
*/
|
||||
static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
|
||||
struct device *dev)
|
||||
{
|
||||
struct pci_dev *tmp, *parent, *pdev;
|
||||
|
||||
if (!iommu || !dev || !dev_is_pci(dev))
|
||||
return;
|
||||
|
||||
pdev = to_pci_dev(dev);
|
||||
|
||||
/* dependent device detach */
|
||||
tmp = pci_find_upstream_pcie_bridge(pdev);
|
||||
/* Secondary interface's bus number and devfn 0 */
|
||||
if (tmp) {
|
||||
parent = pdev->bus->self;
|
||||
while (parent != tmp) {
|
||||
iommu_detach_dev(iommu, parent->bus->number,
|
||||
parent->devfn);
|
||||
parent = parent->bus->self;
|
||||
}
|
||||
if (pci_is_pcie(tmp)) /* this is a PCIe-to-PCI bridge */
|
||||
iommu_detach_dev(iommu,
|
||||
tmp->subordinate->number, 0);
|
||||
else /* this is a legacy PCI bridge */
|
||||
iommu_detach_dev(iommu, tmp->bus->number,
|
||||
tmp->devfn);
|
||||
}
|
||||
pci_for_each_dma_alias(to_pci_dev(dev), &iommu_detach_dev_cb, iommu);
|
||||
}
|
||||
|
||||
static void domain_remove_one_dev_info(struct dmar_domain *domain,
|
||||
|
@ -4370,99 +4414,42 @@ static int intel_iommu_domain_has_cap(struct iommu_domain *domain,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
|
||||
|
||||
static int intel_iommu_add_device(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct pci_dev *bridge, *dma_pdev = NULL;
|
||||
struct intel_iommu *iommu;
|
||||
struct iommu_group *group;
|
||||
int ret;
|
||||
u8 bus, devfn;
|
||||
|
||||
if (!device_to_iommu(dev, &bus, &devfn))
|
||||
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||
if (!iommu)
|
||||
return -ENODEV;
|
||||
|
||||
bridge = pci_find_upstream_pcie_bridge(pdev);
|
||||
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);
|
||||
iommu_device_link(iommu->iommu_dev, dev);
|
||||
|
||||
/* Account for quirked devices */
|
||||
swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
|
||||
group = iommu_group_get_for_dev(dev);
|
||||
|
||||
/*
|
||||
* 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 = iommu_group_get(&dma_pdev->dev);
|
||||
pci_dev_put(dma_pdev);
|
||||
if (!group) {
|
||||
group = iommu_group_alloc();
|
||||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
}
|
||||
|
||||
ret = iommu_group_add_device(group, dev);
|
||||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
|
||||
iommu_group_put(group);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void intel_iommu_remove_device(struct device *dev)
|
||||
{
|
||||
struct intel_iommu *iommu;
|
||||
u8 bus, devfn;
|
||||
|
||||
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||
if (!iommu)
|
||||
return;
|
||||
|
||||
iommu_group_remove_device(dev);
|
||||
|
||||
iommu_device_unlink(iommu->iommu_dev, dev);
|
||||
}
|
||||
|
||||
static struct iommu_ops intel_iommu_ops = {
|
||||
static const struct iommu_ops intel_iommu_ops = {
|
||||
.domain_init = intel_iommu_domain_init,
|
||||
.domain_destroy = intel_iommu_domain_destroy,
|
||||
.attach_dev = intel_iommu_attach_device,
|
||||
|
|
|
@ -369,29 +369,52 @@ static int set_hpet_sid(struct irte *irte, u8 id)
|
|||
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)
|
||||
{
|
||||
struct pci_dev *bridge;
|
||||
struct set_msi_sid_data data;
|
||||
|
||||
if (!irte || !dev)
|
||||
return -1;
|
||||
|
||||
/* PCIe device or Root Complex integrated PCI device */
|
||||
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;
|
||||
}
|
||||
pci_for_each_dma_alias(dev, set_msi_sid_cb, &data);
|
||||
|
||||
bridge = pci_find_upstream_pcie_bridge(dev);
|
||||
if (bridge) {
|
||||
if (pci_is_pcie(bridge))/* this is a PCIe-to-PCI/PCIX bridge */
|
||||
set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
|
||||
(bridge->bus->number << 8) | dev->bus->number);
|
||||
else /* this is a legacy PCI bridge */
|
||||
set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
|
||||
(bridge->bus->number << 8) | bridge->devfn);
|
||||
}
|
||||
/*
|
||||
* DMA alias provides us with a PCI device and alias. The only case
|
||||
* where the it will return an alias on a different bus than the
|
||||
* device is the case of a PCIe-to-PCI bridge, where the alias is for
|
||||
* the subordinate bus. In this case we can only verify the bus.
|
||||
*
|
||||
* If the alias device is on a different bus than our source device
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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 @@
|
|||
#include <linux/idr.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pci.h>
|
||||
#include <trace/events/iommu.h>
|
||||
|
||||
static struct kset *iommu_group_kset;
|
||||
static struct ida iommu_group_ida;
|
||||
static struct mutex iommu_group_mutex;
|
||||
|
||||
struct iommu_callback_data {
|
||||
const struct iommu_ops *ops;
|
||||
};
|
||||
|
||||
struct iommu_group {
|
||||
struct kobject kobj;
|
||||
struct kobject *devices_kobj;
|
||||
|
@ -514,9 +519,191 @@ int iommu_group_id(struct iommu_group *group)
|
|||
}
|
||||
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)
|
||||
{
|
||||
struct iommu_ops *ops = data;
|
||||
struct iommu_callback_data *cb = data;
|
||||
const struct iommu_ops *ops = cb->ops;
|
||||
|
||||
if (!ops->add_device)
|
||||
return -ENODEV;
|
||||
|
@ -532,7 +719,7 @@ static int iommu_bus_notifier(struct notifier_block *nb,
|
|||
unsigned long action, void *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;
|
||||
unsigned long group_action = 0;
|
||||
|
||||
|
@ -585,10 +772,14 @@ static struct notifier_block iommu_bus_nb = {
|
|||
.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_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)
|
|||
* is set up. With this function the iommu-driver can set the iommu-ops
|
||||
* 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)
|
||||
return -EBUSY;
|
||||
|
|
|
@ -1120,7 +1120,7 @@ static void ipmmu_remove_device(struct device *dev)
|
|||
dev->archdata.iommu = NULL;
|
||||
}
|
||||
|
||||
static struct iommu_ops ipmmu_ops = {
|
||||
static const struct iommu_ops ipmmu_ops = {
|
||||
.domain_init = ipmmu_domain_init,
|
||||
.domain_destroy = ipmmu_domain_destroy,
|
||||
.attach_dev = ipmmu_attach_device,
|
||||
|
|
|
@ -674,7 +674,7 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct iommu_ops msm_iommu_ops = {
|
||||
static const struct iommu_ops msm_iommu_ops = {
|
||||
.domain_init = msm_iommu_domain_init,
|
||||
.domain_destroy = msm_iommu_domain_destroy,
|
||||
.attach_dev = msm_iommu_attach_dev,
|
||||
|
|
|
@ -1291,7 +1291,7 @@ static void omap_iommu_remove_device(struct device *dev)
|
|||
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_destroy = omap_iommu_domain_destroy,
|
||||
.attach_dev = omap_iommu_attach_dev,
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* 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)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct iommu_ops shmobile_iommu_ops = {
|
||||
static const struct iommu_ops shmobile_iommu_ops = {
|
||||
.domain_init = shmobile_iommu_domain_init,
|
||||
.domain_destroy = shmobile_iommu_domain_destroy,
|
||||
.attach_dev = shmobile_iommu_attach_device,
|
||||
|
|
|
@ -309,7 +309,7 @@ static int gart_iommu_domain_has_cap(struct iommu_domain *domain,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct iommu_ops gart_iommu_ops = {
|
||||
static const struct iommu_ops gart_iommu_ops = {
|
||||
.domain_init = gart_iommu_domain_init,
|
||||
.domain_destroy = gart_iommu_domain_destroy,
|
||||
.attach_dev = gart_iommu_attach_dev,
|
||||
|
|
|
@ -947,7 +947,7 @@ static void smmu_iommu_domain_destroy(struct iommu_domain *domain)
|
|||
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_destroy = smmu_iommu_domain_destroy,
|
||||
.attach_dev = smmu_iommu_attach_dev,
|
||||
|
|
|
@ -124,7 +124,7 @@ struct bus_type {
|
|||
|
||||
const struct dev_pm_ops *pm;
|
||||
|
||||
struct iommu_ops *iommu_ops;
|
||||
const struct iommu_ops *iommu_ops;
|
||||
|
||||
struct subsys_private *p;
|
||||
struct lock_class_key lock_key;
|
||||
|
|
|
@ -336,6 +336,7 @@ struct intel_iommu {
|
|||
#ifdef CONFIG_IRQ_REMAP
|
||||
struct ir_table *ir_table; /* Interrupt remapping info */
|
||||
#endif
|
||||
struct device *iommu_dev; /* IOMMU-sysfs device */
|
||||
int node;
|
||||
};
|
||||
|
||||
|
@ -365,4 +366,6 @@ extern int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu);
|
|||
|
||||
extern int dmar_ir_support(void);
|
||||
|
||||
extern const struct attribute_group *intel_iommu_groups[];
|
||||
|
||||
#endif
|
||||
|
|
|
@ -50,7 +50,7 @@ struct iommu_domain_geometry {
|
|||
};
|
||||
|
||||
struct iommu_domain {
|
||||
struct iommu_ops *ops;
|
||||
const struct iommu_ops *ops;
|
||||
void *priv;
|
||||
iommu_fault_handler_t handler;
|
||||
void *handler_token;
|
||||
|
@ -140,7 +140,7 @@ struct iommu_ops {
|
|||
#define IOMMU_GROUP_NOTIFY_UNBIND_DRIVER 5 /* Pre 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 struct iommu_domain *iommu_domain_alloc(struct bus_type *bus);
|
||||
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,
|
|||
extern int iommu_group_unregister_notifier(struct iommu_group *group,
|
||||
struct notifier_block *nb);
|
||||
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,
|
||||
void *data);
|
||||
extern int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr,
|
||||
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 */
|
||||
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,
|
|||
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 /* __LINUX_IOMMU_H */
|
||||
|
|
Loading…
Reference in New Issue