[PATCH v3 5/5] iommu/amd - Add a debugfs entry to specify a IOMMU device table entry
From: Gary R Hook
Date: Wed Mar 14 2018 - 19:05:50 EST
Initially (at boot) the device table values dumped are all of the
active devices. Add a devid debugfs file to allow the user to select a
single device table entry to dump (active or not). Let any devid value
greater than the maximum allowable PCI ID (0xFFFF) restore the
behavior to that effective at boot.
Signed-off-by: Gary R Hook <gary.hook@xxxxxxx>
---
drivers/iommu/amd_iommu_debugfs.c | 121 ++++++++++++++++++++++++++++++++-----
1 file changed, 106 insertions(+), 15 deletions(-)
diff --git a/drivers/iommu/amd_iommu_debugfs.c b/drivers/iommu/amd_iommu_debugfs.c
index 1d941c5329be..47bf718f6178 100644
--- a/drivers/iommu/amd_iommu_debugfs.c
+++ b/drivers/iommu/amd_iommu_debugfs.c
@@ -42,6 +42,7 @@ static DEFINE_MUTEX(iommu_debugfs_lock);
#define MAX_NAME_LEN 20
static unsigned int amd_iommu_verbose = 0;
+static unsigned int amd_iommu_devid = ~0;
static unsigned int amd_iommu_count_valid_dtes(int start, int end)
{
@@ -92,14 +93,84 @@ static const struct file_operations amd_iommu_debugfs_dtecount_ops = {
.write = NULL,
};
+static ssize_t amd_iommu_debugfs_devid_read(struct file *filp,
+ char __user *ubuf,
+ size_t count, loff_t *offp)
+{
+ unsigned int obuflen = 64;
+ unsigned int oboff = 0;
+ ssize_t ret;
+ char *obuf;
+
+ obuf = kmalloc(OBUFLEN, GFP_KERNEL);
+ if (!obuf)
+ return -ENOMEM;
+
+ if (amd_iommu_verbose)
+ oboff += OSCNPRINTF("%02x:%02x:%x (%u / %04x)\n",
+ PCI_BUS_NUM(amd_iommu_devid),
+ PCI_SLOT(amd_iommu_devid),
+ PCI_FUNC(amd_iommu_devid),
+ amd_iommu_devid, amd_iommu_devid);
+ else
+ oboff += OSCNPRINTF("%u\n", amd_iommu_devid);
+
+ ret = simple_read_from_buffer(ubuf, count, offp, obuf, oboff);
+ kfree(obuf);
+
+ return ret;
+}
+
+static ssize_t amd_iommu_debugfs_devid_write(struct file *filp,
+ const char __user *ubuf,
+ size_t count, loff_t *offp)
+{
+ unsigned int pci_id, pci_slot, pci_func;
+ unsigned int obuflen = 80;
+ ssize_t ret;
+ char *obuf;
+
+ obuf = kmalloc(OBUFLEN, GFP_KERNEL);
+ if (!obuf)
+ return -ENOMEM;
+
+ ret = simple_write_to_buffer(obuf, OBUFLEN, offp, ubuf, count);
+
+ if (strnchr(obuf, OBUFLEN, ':')) {
+ int n;
+ n = sscanf(obuf, "%x:%x.%x", &pci_id, &pci_slot, &pci_func);
+ if (n == 3)
+ amd_iommu_devid = PCI_DEVID(pci_id, PCI_DEVFN(pci_slot, pci_func));
+ } else {
+ kstrtoint(obuf, 0, &amd_iommu_devid);
+ }
+
+ kfree(obuf);
+
+ return ret;
+}
+
+static const struct file_operations amd_iommu_debugfs_devid_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = amd_iommu_debugfs_devid_read,
+ .write = amd_iommu_debugfs_devid_write,
+};
+
#define MAX_PCI_ID 0xFFFF
-#define PRINTDTE(i) OSCNPRINTF("%02x:%02x:%x - %016llx %016llx %016llx %016llx\n", \
- PCI_BUS_NUM(i), PCI_SLOT(i), PCI_FUNC(i), \
- amd_iommu_dev_table[i].data[0], \
- amd_iommu_dev_table[i].data[1], \
- amd_iommu_dev_table[i].data[2], \
- amd_iommu_dev_table[i].data[3]);
+static inline int amd_iommu_debugfs_printdte(int i, char *obuf, unsigned int obuflen, unsigned int oboff)
+{
+ int rc;
+
+ rc = OSCNPRINTF("%02x:%02x:%x - %016llx %016llx %016llx %016llx\n",
+ PCI_BUS_NUM(i), PCI_SLOT(i), PCI_FUNC(i),
+ amd_iommu_dev_table[i].data[0],
+ amd_iommu_dev_table[i].data[1],
+ amd_iommu_dev_table[i].data[2],
+ amd_iommu_dev_table[i].data[3]);
+ return rc;
+}
static ssize_t amd_iommu_debugfs_dte_read(struct file *filp,
char __user *ubuf,
@@ -113,19 +184,28 @@ static ssize_t amd_iommu_debugfs_dte_read(struct file *filp,
char *obuf;
/* Count the number of valid entries in the device table */
- istart = 0;
- iend = MAX_PCI_ID;
- n = amd_iommu_count_valid_dtes(istart, iend);
+ if (amd_iommu_devid > MAX_PCI_ID) {
+ istart = 0;
+ iend = MAX_PCI_ID;
+ n = amd_iommu_count_valid_dtes(istart, iend);
+ } else {
+ n = 1;
+ }
obuflen = n * 80;
obuf = kmalloc(OBUFLEN, GFP_KERNEL);
if (!obuf)
return -ENOMEM;
- for (i = istart ; i <= iend ; i++)
- if ((amd_iommu_dev_table[i].data[0] ^ 0x3)
- || amd_iommu_dev_table[i].data[1])
- oboff += PRINTDTE(i);
+ if (amd_iommu_devid > MAX_PCI_ID) {
+ for (i = istart ; i <= iend ; i++)
+ if ((amd_iommu_dev_table[i].data[0] ^ 0x3)
+ || amd_iommu_dev_table[i].data[1])
+ oboff += amd_iommu_debugfs_printdte(i, obuf, obuflen, oboff);
+ } else {
+ i = amd_iommu_devid;
+ oboff += amd_iommu_debugfs_printdte(i, obuf, obuflen, oboff);
+ }
ret = simple_read_from_buffer(ubuf, count, offp, obuf, oboff);
kfree(obuf);
@@ -142,12 +222,17 @@ static const struct file_operations amd_iommu_debugfs_dte_ops = {
static char readmetext[] =
"devicetable Print active entries in the device table\n"
+"devid Controls which device IDs are printed\n"
"count Count of active devices\n"
"verbose Provide additional descriptive text\n"
"\n"
" Dumping the Device Table\n"
-"The device table is scanned for entries that appear to be active. The\n"
-"default range is from 0 to 0xFFFF, and only active entries will be reported\n"
+"The device table is scanned for entries that appear to be active.\n"
+"The default (initial) range is from 0 to 0xFFFF, represented by a devid\n"
+"value greater than 0xFFFF, and only active entries will be reported.\n"
+"If devid is set to a specific value, only that device entry will be\n"
+"displayed (active or not). devid may be specified as ##:##:#, a decimal\n"
+"value, or a hex value.\n"
"\n";
static ssize_t amd_iommu_debugfs_readme_read(struct file *filp,
@@ -210,6 +295,12 @@ void amd_iommu_debugfs_setup(struct amd_iommu *iommu)
if (!d_dte)
goto err;
+ d_dte = debugfs_create_file("devid", 0400,
+ iommu->debugfs_instance, iommu,
+ &amd_iommu_debugfs_devid_ops);
+ if (!d_dte)
+ goto err;
+
d_dte = debugfs_create_file("README", 0400,
iommu->debugfs_instance, iommu,
&amd_iommu_debugfs_readme_ops);