diff options
author | Hiroshi Doyu <hdoyu@nvidia.com> | 2014-04-03 13:11:36 +0300 |
---|---|---|
committer | Hiroshi Doyu <hdoyu@nvidia.com> | 2014-04-06 23:10:25 -0700 |
commit | f585255e260658b2fc62023bb7f99b49866395bf (patch) | |
tree | a55d7cf2ea7f24047ce953a13ba19b694c375193 /drivers | |
parent | 0c3682793be78c6e3c32b5461b16ffe7e5008de0 (diff) |
iommu/tegra: smmu: add dump IOMMU pagetable
Add pagetable dump feature in debugfs. SMMU has a pagetable per address
space(AS). There are multiple pagetables, page_tables<ASID#>. This also
shows attribute, 'R'eadable, 'W'ritable and 'N'onsecure.
For exmaple,
$ cat /sys/kernel/debug/tegra_smmu/page_tables0001
..........
0x80008000-0x8000b000 12K RW-
Bug 1399946
Change-Id: I53ddac6cbc4b5ef6fb05daddd6a1b019ded2896d
Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
Reviewed-on: http://git-master/r/392353
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/iommu/tegra-smmu.c | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 48cbb62037d8..9e3c7b2cbc88 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -1627,6 +1627,135 @@ static void smmu_debugfs_delete(struct smmu_device *smmu) kfree(smmu->debugfs_info); } +struct smmu_addr_marker { + u32 start_address; + const char *name; +}; + +#define SZ_3G (SZ_1G + SZ_2G) +static struct smmu_addr_marker address_markers[] = { + { 0, "0x0000:0000", }, + { SZ_1G, "0x4000:0000", }, + { SZ_2G, "0x8000:0000", }, + { SZ_3G, "0xc000:0000", }, + { -1, NULL, }, +}; + +struct smmu_pg_state { + struct seq_file *seq; + const struct smmu_addr_marker *marker; + u32 start_address; + unsigned level; + u32 current_prot; +}; + +static void smmu_dump_attr(struct smmu_pg_state *st) +{ + int i; + const char prot_set[] = "RW-"; + const char prot_clr[] = "--S"; + + for (i = 0; i < ARRAY_SIZE(prot_set); i++) { + if (st->current_prot & BIT(31 - i)) + seq_printf(st->seq, "%c", prot_set[i]); + else + seq_printf(st->seq, "%c", prot_clr[i]); + } +} + +static void smmu_note_page(struct smmu_pg_state *st, u32 addr, int level, + u32 val) +{ + static const char units[] = "KMGTPE"; + u32 prot = val & _MASK_ATTR; + + if (!st->level) { + st->level = level; + st->current_prot = prot; + seq_printf(st->seq, "---[ %s ]---\n", st->marker->name); + } else if (prot != st->current_prot || level != st->level || + addr >= st->marker[1].start_address) { + const char *unit = units; + unsigned long delta; + + if (st->current_prot) { + seq_printf(st->seq, "0x%08x-0x%08x ", + st->start_address, addr); + + delta = (addr - st->start_address) >> 10; + while (!(delta & 1023) && unit[1]) { + delta >>= 10; + unit++; + } + seq_printf(st->seq, "%9lu%c ", delta, *unit); + smmu_dump_attr(st); + seq_puts(st->seq, "\n"); + } + + if (addr >= st->marker[1].start_address) { + st->marker++; + seq_printf(st->seq, "---[ %s ]---\n", st->marker->name); + } + + st->start_address = addr; + st->current_prot = prot; + st->level = level; + } +} + +static void smmu_walk_pte(struct smmu_pg_state *st, u32 *pgd, u32 start) +{ + int i; + u32 *pte = page_address(SMMU_EX_PTBL_PAGE(*pgd)); + + for (i = 0; i < PTRS_PER_PTE; i++, pte++) + smmu_note_page(st, start + i * PAGE_SIZE, 2, *pte); +} + +static void smmu_walk_pgd(struct seq_file *m, struct smmu_as *as) +{ + int i; + u32 *pgd; + struct smmu_pg_state st = { + .seq = m, + .marker = address_markers, + }; + + if (!pfn_valid(page_to_pfn(as->pdir_page))) + return; + + pgd = page_address(as->pdir_page); + for (i = 0; i < SMMU_PDIR_COUNT; i++, pgd++) { + u32 addr = i * SMMU_PAGE_SIZE * SMMU_PTBL_COUNT; + + if (*pgd & _PDE_NEXT) + smmu_walk_pte(&st, pgd, addr); + else + smmu_note_page(&st, addr, 1, *pgd); + } + + smmu_note_page(&st, 0, 0, 0); +} + +static int smmu_ptdump_show(struct seq_file *m, void *v) +{ + smmu_walk_pgd(m, m->private); + return 0; +} + +static int smmu_ptdump_open(struct inode *inode, struct file *file) +{ + return single_open(file, smmu_ptdump_show, inode->i_private); +} + +static const struct file_operations smmu_ptdump_fops = { + .open = smmu_ptdump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + static void smmu_debugfs_create(struct smmu_device *smmu) { int i; @@ -1644,6 +1773,17 @@ static void smmu_debugfs_create(struct smmu_device *smmu) goto err_out; smmu->debugfs_root = root; + for (i = 0; i < smmu->num_as; i++) { + struct dentry *pt; + char name[20]; + + sprintf(name, "page_tables%03d", i); + pt = debugfs_create_file(name, 0400, smmu->debugfs_root, + smmu->as + i, &smmu_ptdump_fops); + if (!pt) + goto err_out; + } + for (i = 0; i < ARRAY_SIZE(smmu_debugfs_mc); i++) { int j; struct dentry *mc; |