[RFC PATCH 5/5] iommu/arm-smmu-v3: Add Context Descriptor display to debugfs
Qinxin Xia
xiaqinxin at huawei.com
Mon Mar 16 19:14:57 PDT 2026
On 2026/3/16 23:42:36, Robin Murphy <robin.murphy at arm.com> wrote:
> On 2026-03-13 10:43 am, Qinxin Xia wrote:
>> Add Context Descriptor (CD) display functionality to debugfs.
>> This allow inspecting CD contents for all Substream IDs including:
>> - CD validity and translation parameters
>> - TTBR0 and TCR configurations
>> - Raw CD data
>>
>> /sys/kernel/debug/iommu/arm_smmu_v3/smmu0/stream_table/
>> └── 0000:01:00.0:0/
>> ├── ste
>> └── context_descriptors/
>> └── all
>>
>> Signed-off-by: Qinxin Xia <xiaqinxin at huawei.com>
>> ---
>> .../arm/arm-smmu-v3/arm-smmu-v3-debugfs.c | 112 ++++++++++++++++++
>> 1 file changed, 112 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 f62df02847ac..66ae1228c6ad 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
>> @@ -27,6 +27,13 @@
>> * - Context Pointers: Stage 1 and Stage 2 translation context
>> addresses
>> * - Raw Data: Complete 64-bit STE words in hexadecimal
>> *
>> + * CD Information Displayed:
>> + * - Validity: Active state of the context descriptor
>> + * - T0SZ: Input address space size configuration
>> + * - EPD0/EPD1: Stage 1 translation enable flags
>> + * - TTBR0: Stage 1 translation table base address
>> + * - Raw Data: Complete CD structure in hexadecimal format
>> + *
>> * Directory Structure:
>> * /sys/kernel/debug/iommu/arm_smmu_v3/
>> * └── smmu0/
>> @@ -35,6 +42,8 @@
>> * └── stream_table
>> * └── 0000:01:00.0:0/ # PCI device with
>> Stream ID 0
>> * ├── ste # Stream Table Entry
>> + * └── context_descriptors/
>> + * └── all # All Context
>> Descriptors
>> *
>> * The capabilities file provides detailed information about:
>> * - Architecture version and translation stage support (Stage1/Stage2)
>> @@ -59,6 +68,8 @@
>> static struct dentry *smmuv3_root_dir;
>> static DEFINE_MUTEX(arm_smmu_debugfs_lock);
>> +#define MAX_SSIDS 32 /* Reasonable limit for SSID enumeration */
>
> Arbitrarily limiting to only 32 out of a potential 2^20 SSIDs seems odd,
> like it's rather more than needed for basic sanity-checking, but far too
> few for debugging specific usage.
>
> FWIW I'd imagine it might be more useful to expose the CDs as individual
> files by index - and if userspace does want an "all" dump then there's
> always `cat`. Whether that would be all of them unconditionally based on
> the current STE.S1CDMAX, or just the "active" ones based on PASID
> allocations and S1DSS, is probably a question of what people think is
> most useful (and/or how fiddly it is to actually implement the latter).
>
I think 'active' may cover most debug scenarios.In the next version,
I'll remove the limit here.
>> +
>> /**
>> * smmu_debugfs_capabilities_show() - Display SMMU capabilities
>> * @seq: seq_file to write to
>> @@ -329,6 +340,90 @@ static int smmu_debugfs_ste_show(struct seq_file
>> *seq, void *v)
>> }
>> DEFINE_SHOW_ATTRIBUTE(smmu_debugfs_ste);
>> +/**
>> + * smmu_debug_dump_cd() - Dump Context Descriptor details to seq_file
>> + * @seq: seq_file to write to
>> + * @dev: device associated with the CD
>> + * @ssid: Substream ID
>> + */
>> +static void smmu_debug_dump_cd(struct seq_file *seq, struct device
>> *dev, u32 ssid)
>> +{
>> + struct arm_smmu_master *master = dev_iommu_priv_get(dev);
>> + struct arm_smmu_cd *cd;
>> + u64 data;
>> + int i;
>> +
>> + if (!master) {
>> + seq_puts(seq, "No master data\n");
>> + return;
>> + }
>
> If userspace can read these files racily with devices being removed then:
>
> 1) we should probably return an actual read error if the device has
> already disappeared such the file is no longer valid.
>
> 2) how do we know the device isn't going to disappear, and thus "master"
> become invalid, immediately *after* this check happens to pass?
>
> Thanks,
> Robin.
>
Thank you for your review.
In the next version, I'll use lock protection and check the master again
in the lock.
>> +
>> + cd = arm_smmu_get_cd_ptr(master, ssid);
>> + if (!cd) {
>> + seq_printf(seq, "CD not available for SSID %u\n", ssid);
>> + return;
>> + }
>> +
>> + seq_printf(seq, "CD for Substream ID %u:\n", ssid);
>> +
>> + /* CD 0 */
>> + data = le64_to_cpu(cd->data[0]);
>> + seq_printf(seq, " Valid: %s\n", data & CTXDESC_CD_0_V ? "Yes" :
>> "No");
>> + seq_printf(seq, " T0SZ: 0x%llx\n", data & CTXDESC_CD_0_TCR_T0SZ);
>> + seq_printf(seq, " EPD0: %s\n", data & CTXDESC_CD_0_TCR_EPD0 ?
>> "Yes" : "No");
>> + seq_printf(seq, " EPD1: %s\n", data & CTXDESC_CD_0_TCR_EPD1 ?
>> "Yes" : "No");
>> +
>> + /* CD 1 */
>> + data = le64_to_cpu(cd->data[1]);
>> + seq_printf(seq, " TTBR0: 0x%016llx\n", data &
>> CTXDESC_CD_1_TTB0_MASK);
>> +
>> + /* Display raw CD data */
>> + seq_puts(seq, " Raw Data:\n");
>> + for (i = 0; i < CTXDESC_CD_DWORDS; i++)
>> + seq_printf(seq, " CD[%d]: 0x%016llx\n", i,
>> + le64_to_cpu(cd->data[i]));
>> +}
>> +
>> +/**
>> + * smmu_debug_dump_all_cds() - Dump all valid Context Descriptors for
>> a device
>> + * @seq: seq_file to write to
>> + * @dev: target device
>> + */
>> +static void smmu_debug_dump_all_cds(struct seq_file *seq, struct
>> device *dev)
>> +{
>> + struct arm_smmu_master *master = dev_iommu_priv_get(dev);
>> + u32 max_ssids, ssid;
>> +
>> + if (!master) {
>> + seq_puts(seq, "No master data\n");
>> + return;
>> + }
>> +
>> + max_ssids = min_t(u32, 1 << master->ssid_bits, MAX_SSIDS);
>> +
>> + seq_printf(seq, "Context Descriptors for device (max SSIDs: %u):\n",
>> + max_ssids);
>> +
>> + for (ssid = 0; ssid < max_ssids; ssid++) {
>> + struct arm_smmu_cd *cd = arm_smmu_get_cd_ptr(master, ssid);
>> +
>> + if (cd && (le64_to_cpu(cd->data[0]) & CTXDESC_CD_0_V)) {
>> + seq_printf(seq, "\n--- SSID %u ---\n", ssid);
>> + smmu_debug_dump_cd(seq, dev, ssid);
>> + }
>> + }
>> +}
>> +
>> +/* All CDs debugfs file operations */
>> +static int smmu_debugfs_all_cds_show(struct seq_file *seq, void *v)
>> +{
>> + struct device *dev = seq->private;
>> +
>> + smmu_debug_dump_all_cds(seq, dev);
>> + return 0;
>> +}
>> +DEFINE_SHOW_ATTRIBUTE(smmu_debugfs_all_cds);
>> +
>> /**
>> * smmu_debugfs_create_stream_table() - Create debugfs entries for
>> stream table
>> * @dev: device to create entries for
>> @@ -388,9 +483,26 @@ int smmu_debugfs_create_stream_table(struct
>> device *dev,
>> goto cleanup_dev;
>> }
>> + /* Create CD directory */
>> + cd_dir = debugfs_create_dir("context_descriptors", dev_dir);
>> + if (!cd_dir) {
>> + ret = -ENOMEM;
>> + goto cleanup_dev;
>> + }
>> +
>> + /* Create "all" file to show all valid CDs */
>> + all_cds_file = debugfs_create_file("all", 0444, cd_dir, dev,
>> + &smmu_debugfs_all_cds_fops);
>> + if (!all_cds_file) {
>> + ret = -ENOMEM;
>> + goto cleanup_cd;
>> + }
>> +
>> /* Success for this stream ID, continue to next */
>> continue;
>> +cleanup_cd:
>> + debugfs_remove(cd_dir);
>> cleanup_dev:
>> debugfs_remove_recursive(dev_dir);
>> cleanup:
>
>
--
Thanks,
Qinxin
More information about the linux-arm-kernel
mailing list