[PATCH v2 1/5] iommu/amd - Add debugfs support

From: Gary R Hook
Date: Thu Mar 08 2018 - 19:51:04 EST


Expose the IOMMU MMIO registers and device table

Signed-off-by: Gary R Hook <gary.hook@xxxxxxx>
---
drivers/iommu/Kconfig | 8 +++
drivers/iommu/Makefile | 1
drivers/iommu/amd_iommu_debugfs.c | 112 +++++++++++++++++++++++++++++++++++++
drivers/iommu/amd_iommu_init.c | 7 ++
drivers/iommu/amd_iommu_proto.h | 7 ++
drivers/iommu/amd_iommu_types.h | 3 +
6 files changed, 136 insertions(+), 2 deletions(-)
create mode 100644 drivers/iommu/amd_iommu_debugfs.c

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index f3a21343e636..0fa85e73d5dd 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -135,6 +135,14 @@ config AMD_IOMMU_V2
hardware. Select this option if you want to use devices that support
the PCI PRI and PASID interface.

+config AMD_IOMMU_DEBUG
+ bool "Expose AMD IOMMU internals in DebugFS"
+ depends on AMD_IOMMU && DEBUG_FS
+ default n
+ help
+ Provides debugfs access to IOMMU data such as registers and device
+ table entries.
+
# Intel IOMMU support
config DMAR_TABLE
bool
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 1fb695854809..64fba8b1ca4f 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_IOMMU_IOVA) += iova.o
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
+obj-$(CONFIG_AMD_IOMMU_DEBUG) += amd_iommu_debugfs.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
diff --git a/drivers/iommu/amd_iommu_debugfs.c b/drivers/iommu/amd_iommu_debugfs.c
new file mode 100644
index 000000000000..b0395a47bc32
--- /dev/null
+++ b/drivers/iommu/amd_iommu_debugfs.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD IOMMU driver
+ *
+ * Copyright (C) 2018 Advanced Micro Devices, Inc.
+ *
+ * Author: Gary R Hook <gary.hook@xxxxxxx>
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/pci.h>
+#include <linux/iommu.h>
+#include <linux/debugfs.h>
+#include "amd_iommu_proto.h"
+#include "amd_iommu_types.h"
+
+/* DebugFS helpers */
+#define OBUFP (obuf + oboff)
+#define OBUFLEN obuflen
+#define OBUFSPC (OBUFLEN - oboff)
+#define OSCNPRINTF(fmt, ...) \
+ scnprintf(OBUFP, OBUFSPC, fmt, ## __VA_ARGS__)
+
+static struct dentry *iommu_debugfs_dir;
+static DEFINE_RWLOCK(iommu_debugfs_lock);
+
+#define MAX_NAME_LEN 20
+
+static unsigned int amd_iommu_count_valid_dtes(int start, int end)
+{
+ unsigned int n = 0;
+ int i;
+
+ /* Scan the DTE table from entry 'start' through entry 'end' for
+ * active entries
+ */
+ for (i = start ; i <= end ; i++)
+ if ((amd_iommu_dev_table[i].data[0] ^ 0x3)
+ || amd_iommu_dev_table[i].data[1])
+ n++;
+ return n;
+}
+
+static ssize_t amd_iommu_debugfs_dtecount_read(struct file *filp,
+ char __user *ubuf,
+ size_t count, loff_t *offp)
+{
+ struct amd_iommu *iommu = filp->private_data;
+ unsigned int obuflen = 512;
+ unsigned int oboff = 0;
+ unsigned int n;
+ ssize_t ret;
+ char *obuf;
+
+ if (!iommu)
+ return 0;
+
+ obuf = kmalloc(OBUFLEN, GFP_KERNEL);
+ if (!obuf)
+ return -ENOMEM;
+
+ n = amd_iommu_count_valid_dtes(0, 0xFFFF);
+ oboff += OSCNPRINTF("%d\n", n);
+
+ ret = simple_read_from_buffer(ubuf, count, offp, obuf, oboff);
+ kfree(obuf);
+
+ return ret;
+}
+
+static const struct file_operations amd_iommu_debugfs_dtecount_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = amd_iommu_debugfs_dtecount_read,
+ .write = NULL,
+};
+
+void amd_iommu_debugfs_setup(struct amd_iommu *iommu)
+{
+ char name[MAX_NAME_LEN + 1];
+ struct dentry *d_dte;
+ unsigned long flags;
+
+ if (!debugfs_initialized())
+ return;
+
+ write_lock_irqsave(&iommu_debugfs_lock, flags);
+ if (!iommu_debugfs_dir)
+ iommu_debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ write_unlock_irqrestore(&iommu_debugfs_lock, flags);
+ if (!iommu_debugfs_dir)
+ goto err;
+
+ snprintf(name, MAX_NAME_LEN, "iommu%02x", iommu->index);
+ iommu->debugfs_instance = debugfs_create_dir(name, iommu_debugfs_dir);
+ if (!iommu->debugfs_instance)
+ goto err;
+
+ d_dte = debugfs_create_file("count", 0400,
+ iommu->debugfs_instance, iommu,
+ &amd_iommu_debugfs_dtecount_ops);
+ if (!d_dte)
+ goto err;
+
+ return;
+
+err:
+ debugfs_remove_recursive(iommu->debugfs_instance);
+}
+
+#endif
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 4e4a615bf13f..501433c29a3e 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -89,6 +89,7 @@
#define ACPI_DEVFLAG_ATSDIS 0x10000000

#define LOOP_TIMEOUT 100000
+
/*
* ACPI table definitions
*
@@ -2721,6 +2722,7 @@ int __init amd_iommu_enable_faulting(void)
*/
static int __init amd_iommu_init(void)
{
+ struct amd_iommu *iommu;
int ret;

ret = iommu_go_to_state(IOMMU_INITIALIZED);
@@ -2730,14 +2732,15 @@ static int __init amd_iommu_init(void)
disable_iommus();
free_iommu_resources();
} else {
- struct amd_iommu *iommu;
-
uninit_device_table_dma();
for_each_iommu(iommu)
iommu_flush_all_caches(iommu);
}
}

+ for_each_iommu(iommu)
+ amd_iommu_debugfs_setup(iommu);
+
return ret;
}

diff --git a/drivers/iommu/amd_iommu_proto.h b/drivers/iommu/amd_iommu_proto.h
index 640c286a0ab9..db5872d3d40f 100644
--- a/drivers/iommu/amd_iommu_proto.h
+++ b/drivers/iommu/amd_iommu_proto.h
@@ -33,6 +33,13 @@ extern void amd_iommu_uninit_devices(void);
extern void amd_iommu_init_notifier(void);
extern int amd_iommu_init_api(void);

+#ifdef CONFIG_AMD_IOMMU_DEBUG
+extern void amd_iommu_debugfs_setup(struct amd_iommu *iommu);
+#else
+static inline void amd_iommu_debugfs_setup(struct amd_iommu *iommu) {}
+#endif
+
+
/* Needed for interrupt remapping */
extern int amd_iommu_prepare(void);
extern int amd_iommu_enable(void);
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index 6a877ebd058b..43c008dc7ade 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -593,6 +593,9 @@ struct amd_iommu {

u32 flags;
volatile u64 __aligned(8) cmd_sem;
+
+ /* DebugFS Info */
+ struct dentry *debugfs_instance;
};

static inline struct amd_iommu *dev_to_amd_iommu(struct device *dev)