[RFC PATCH 3/5] iommu/arm-smmu-v3: Add Stream Table Entry display to debugfs

Qinxin Xia xiaqinxin at huawei.com
Fri Mar 13 03:43:49 PDT 2026


Add Stream Table Entry (STE) display functionality to debugfs.
This allow inspecting STE contents for each device stream including:
- STE validity and configuration
- Stage 1 and Stage 2 context pointers
- Raw STE data

Signed-off-by: Qinxin Xia <xiaqinxin at huawei.com>
---
 .../arm/arm-smmu-v3/arm-smmu-v3-debugfs.c     | 130 ++++++++++++++++++
 1 file changed, 130 insertions(+)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-debugfs.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-debugfs.c
index f9bf955f3351..d7f3defd94a3 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-debugfs.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-debugfs.c
@@ -21,6 +21,12 @@
  * - CMDQ_PROD/CONS: Command queue producer and consumer pointers
  * - EVTQ_PROD/CONS: Event queue producer and consumer pointers
  *
+ * STE Information Displayed:
+ * - Validity: Whether the STE is currently active and valid
+ * - Configuration: Translation mode (bypass/abort/S1/S2)
+ * - Context Pointers: Stage 1 and Stage 2 translation context addresses
+ * - Raw Data: Complete 64-bit STE words in hexadecimal
+ *
  * Directory Structure:
  * /sys/kernel/debug/iommu/arm_smmu_v3/
  * └── smmu0/
@@ -43,6 +49,8 @@
  */
 
 #include <linux/debugfs.h>
+#include <linux/pci.h>
+#include <linux/iommu.h>
 #include "arm-smmu-v3.h"
 
 static struct dentry *smmuv3_root_dir;
@@ -196,3 +204,125 @@ int arm_smmu_debugfs_setup(struct arm_smmu_device *smmu, phys_addr_t ioaddr)
 	smmu->debugfs = NULL;
 	return ret;
 }
+
+/**
+ * smmu_get_ste() - Get Stream Table Entry for a given Stream ID
+ * @smmu: SMMU device
+ * @sid: Stream ID
+ *
+ * Return: Pointer to STE if found, NULL otherwise
+ */
+static struct arm_smmu_ste *smmu_get_ste(struct arm_smmu_device *smmu, u32 sid)
+{
+	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+	if (sid >= (1 << smmu->sid_bits))
+		return NULL;
+
+	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+		u32 l1_idx = arm_smmu_strtab_l1_idx(sid);
+		u32 l2_idx = arm_smmu_strtab_l2_idx(sid);
+
+		if (l1_idx >= cfg->l2.num_l1_ents || !cfg->l2.l2ptrs[l1_idx])
+			return NULL;
+
+		return &cfg->l2.l2ptrs[l1_idx]->stes[l2_idx];
+	}
+
+	return &cfg->linear.table[sid];
+}
+
+/**
+ * smmu_debug_dump_ste() - Dump STE details to seq_file
+ * @seq: seq_file to write to
+ * @dev: device associated with the STE
+ */
+static void smmu_debug_dump_ste(struct seq_file *seq, struct device *dev)
+{
+	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+	struct arm_smmu_device *smmu;
+	struct arm_smmu_ste *ste;
+	u32 sid, cfg;
+	int i;
+
+	if (!master || !master->smmu) {
+		seq_puts(seq, "No SMMU master data\n");
+		return;
+	}
+
+	smmu = master->smmu;
+
+	/* Use first stream ID for debug */
+	if (master->num_streams == 0) {
+		seq_puts(seq, "No streams configured for device\n");
+		return;
+	}
+	sid = master->streams[0].id;
+
+	if (sid >= (1 << smmu->sid_bits)) {
+		seq_printf(seq, "Invalid Stream ID: %u (max %u)\n",
+			   sid, (1 << smmu->sid_bits) - 1);
+		return;
+	}
+
+	ste = smmu_get_ste(smmu, sid);
+	if (!ste) {
+		seq_printf(seq, "STE not available for SID %u\n", sid);
+		return;
+	}
+
+	seq_printf(seq, "STE for Stream ID %u\n", sid);
+	seq_printf(seq, "  Valid: %s\n",
+		   ste->data[0] & STRTAB_STE_0_V ? "Yes" : "No");
+
+	seq_puts(seq, "  Config: ");
+
+	cfg = FIELD_GET(STRTAB_STE_0_CFG, ste->data[0]);
+
+	switch (cfg) {
+	case STRTAB_STE_0_CFG_BYPASS:
+		seq_puts(seq, "BYPASS\n");
+		break;
+	case STRTAB_STE_0_CFG_S1_TRANS:
+		seq_puts(seq, "only S1_TRANS\n");
+		break;
+	case STRTAB_STE_0_CFG_S2_TRANS:
+		seq_puts(seq, "only S2_TRANS\n");
+		break;
+	case STRTAB_STE_0_CFG_NESTED:
+		seq_puts(seq, "S1+S2_TRANS\n");
+		break;
+	case STRTAB_STE_0_CFG_ABORT:
+		seq_puts(seq, "ABORT\n");
+		break;
+	default:
+		seq_puts(seq, "UNKNOWN\n");
+	}
+
+	if (ste->data[0] & STRTAB_STE_0_CFG_S1_TRANS) {
+		seq_printf(seq, "  S1ContextPtr: 0x%016llx\n",
+			   le64_to_cpu(ste->data[1]) & STRTAB_STE_0_S1CTXPTR_MASK);
+	}
+
+	if (ste->data[0] & STRTAB_STE_0_CFG_S2_TRANS) {
+		seq_printf(seq, "  S2ContextPtr: 0x%016llx\n",
+			   le64_to_cpu(ste->data[3]) & STRTAB_STE_3_S2TTB_MASK);
+	}
+
+	/* Display raw STE data */
+	seq_puts(seq, "  Raw Data:\n");
+	for (i = 0; i < STRTAB_STE_DWORDS; i++)
+		seq_printf(seq, "    STE[%d]: 0x%016llx\n", i,
+			   le64_to_cpu(ste->data[i]));
+}
+
+/* STE debugfs file operations */
+static int smmu_debugfs_ste_show(struct seq_file *seq, void *v)
+{
+	struct device *dev = seq->private;
+
+	smmu_debug_dump_ste(seq, dev);
+	return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(smmu_debugfs_ste);
-- 
2.33.0




More information about the linux-arm-kernel mailing list