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-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)
static int init_iommu_group(struct device *dev)
{
struct iommu_group *group = iommu_group_get(&pdev->dev);
int ret;
struct iommu_group *group;
if (!group) {
group = iommu_group_alloc();
if (IS_ERR(group))
return PTR_ERR(group);
group = iommu_group_get_for_dev(dev);
WARN_ON(&pdev->dev != dev);
}
if (IS_ERR(group))
return PTR_ERR(group);
ret = iommu_group_add_device(group, dev);
iommu_group_put(group);
return ret;
return 0;
}
static int use_dev_data_iommu_group(struct iommu_dev_data *dev_data,
struct device *dev)
static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
{
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);
*(u16 *)data = alias;
return 0;
}
static int init_iommu_group(struct device *dev)
static u16 get_alias(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;
}
dev_data = find_dev_data(get_device_id(dev));
if (!dev_data)
return -ENOMEM;
struct pci_dev *pdev = to_pci_dev(dev);
u16 devid, ivrs_alias, pci_alias;
if (dev_data->alias_data) {
u16 alias;
struct pci_bus *bus;
devid = get_device_id(dev);
ivrs_alias = amd_iommu_alias_table[devid];
pci_for_each_dma_alias(pdev, __last_alias, &pci_alias);
if (dev_data->alias_data->group)
goto use_group;
if (ivrs_alias == pci_alias)
return ivrs_alias;
/*
* 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;
/*
* 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;
return pci_alias;
}
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;
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));
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)));
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);
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);
/* 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));
}
group = iommu_group_get_for_dev(&pdev->dev);
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,
......
This diff is collapsed.
......@@ -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;
}
......
/*
* 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 @@ irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id)
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,
......
/*
* 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 */
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