summaryrefslogtreecommitdiffstats
path: root/drivers/iommu
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2014-06-12 16:12:31 -0600
committerJoerg Roedel <jroedel@suse.de>2014-07-04 12:35:59 +0200
commita5459cfece880e82778a60e6290ad6c0dd688a06 (patch)
treef032c49a317bc56b2d3a9883933b1b154a037fdf /drivers/iommu
parentc61959ecbbc6bf9034e65c8e8ef03fa9d1066f05 (diff)
downloadop-kernel-dev-a5459cfece880e82778a60e6290ad6c0dd688a06.zip
op-kernel-dev-a5459cfece880e82778a60e6290ad6c0dd688a06.tar.gz
iommu/vt-d: Make use of IOMMU sysfs support
Register our DRHD IOMMUs, cross link devices, and provide a base set of attributes for the IOMMU. Note that IRQ remapping support parses the DMAR table very early in boot, well before the iommu_class can reasonably be setup, so our registration is split between intel_iommu_init(), which occurs later, and alloc_iommu(), which typically occurs much earlier, but may happen at any time later with IOMMU hot-add support. On a typical desktop system, this provides the following (pruned): $ find /sys | grep dmar /sys/devices/virtual/iommu/dmar0 /sys/devices/virtual/iommu/dmar0/devices /sys/devices/virtual/iommu/dmar0/devices/0000:00:02.0 /sys/devices/virtual/iommu/dmar0/intel-iommu /sys/devices/virtual/iommu/dmar0/intel-iommu/cap /sys/devices/virtual/iommu/dmar0/intel-iommu/ecap /sys/devices/virtual/iommu/dmar0/intel-iommu/address /sys/devices/virtual/iommu/dmar0/intel-iommu/version /sys/devices/virtual/iommu/dmar1 /sys/devices/virtual/iommu/dmar1/devices /sys/devices/virtual/iommu/dmar1/devices/0000:00:00.0 /sys/devices/virtual/iommu/dmar1/devices/0000:00:01.0 /sys/devices/virtual/iommu/dmar1/devices/0000:00:16.0 /sys/devices/virtual/iommu/dmar1/devices/0000:00:1a.0 /sys/devices/virtual/iommu/dmar1/devices/0000:00:1b.0 /sys/devices/virtual/iommu/dmar1/devices/0000:00:1c.0 ... /sys/devices/virtual/iommu/dmar1/intel-iommu /sys/devices/virtual/iommu/dmar1/intel-iommu/cap /sys/devices/virtual/iommu/dmar1/intel-iommu/ecap /sys/devices/virtual/iommu/dmar1/intel-iommu/address /sys/devices/virtual/iommu/dmar1/intel-iommu/version /sys/class/iommu/dmar0 /sys/class/iommu/dmar1 (devices also link back to the dmar units) This makes address, version, capabilities, and extended capabilities available, just like printed on boot. I've tried not to duplicate data that can be found in the DMAR table, with the exception of the address, which provides an easy way to associate the sysfs device with a DRHD entry in the DMAR. It's tempting to add scopes and RMRR data here, but the full DMAR table is already exposed under /sys/firmware/ and therefore already provides a way for userspace to learn such details. Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Signed-off-by: Joerg Roedel <jroedel@suse.de>
Diffstat (limited to 'drivers/iommu')
-rw-r--r--drivers/iommu/dmar.c9
-rw-r--r--drivers/iommu/intel-iommu.c77
2 files changed, 85 insertions, 1 deletions
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 9a4f05e..6744e2d 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -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);
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index f38ec7a..f9e5f84 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -3944,6 +3944,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;
@@ -4015,6 +4072,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)
@@ -4358,12 +4420,16 @@ static int intel_iommu_domain_has_cap(struct iommu_domain *domain,
static int intel_iommu_add_device(struct device *dev)
{
+ struct intel_iommu *iommu;
struct iommu_group *group;
u8 bus, devfn;
- if (!device_to_iommu(dev, &bus, &devfn))
+ iommu = device_to_iommu(dev, &bus, &devfn);
+ if (!iommu)
return -ENODEV;
+ iommu_device_link(iommu->iommu_dev, dev);
+
group = iommu_group_get_for_dev(dev);
if (IS_ERR(group))
@@ -4375,7 +4441,16 @@ static int intel_iommu_add_device(struct device *dev)
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 = {
OpenPOWER on IntegriCloud