From dd5142ca5d24bcdb22e40add842d50a59ffa769e Mon Sep 17 00:00:00 2001 From: Sai Praneeth Prakhya Date: Fri, 24 May 2019 16:40:17 -0700 Subject: [PATCH] iommu/vt-d: Add debugfs support to show scalable mode DMAR table internals A DMAR table walk would typically follow the below process. 1. Bus number is used to index into root table which points to a context table. 2. Device number and Function number are used together to index into context table which then points to a pasid directory. 3. PASID[19:6] is used to index into PASID directory which points to a PASID table. 4. PASID[5:0] is used to index into PASID table which points to all levels of page tables. Whenever a user opens the file "/sys/kernel/debug/iommu/intel/dmar_translation_struct", the above described DMAR table walk is performed and the contents of the table are dumped into the file. The dump could be handy while dealing with devices that use PASID. Example of such dump: cat /sys/kernel/debug/iommu/intel/dmar_translation_struct (Please note that because of 80 char limit, entries that should have been in the same line are broken into different lines) IOMMU dmar0: Root Table Address: 0x436f7c000 B.D.F Root_entry Context_entry PASID PASID_table_entry 00:0a.0 0x0000000000000000:0x000000044dd3f001 0x0000000000100000:0x0000000435460e1d 0 0x000000044d6e1089:0x0000000000000003:0x0000000000000001 00:0a.0 0x0000000000000000:0x000000044dd3f001 0x0000000000100000:0x0000000435460e1d 1 0x0000000000000049:0x0000000000000001:0x0000000003c0e001 Note that the above format is followed even for legacy DMAR table dump which doesn't support PASID and hence in such cases PASID is defaulted to -1 indicating that PASID and it's related fields are invalid. Cc: Joerg Roedel Cc: Ashok Raj Cc: Lu Baolu Cc: Sohil Mehta Cc: David Woodhouse Cc: Jacob Pan Cc: Andy Shevchenko Reviewed-by: Lu Baolu Reviewed-by: Andy Shevchenko Signed-off-by: Sai Praneeth Prakhya Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu-debugfs.c | 78 +++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/intel-iommu-debugfs.c b/drivers/iommu/intel-iommu-debugfs.c index 3f5399b5e6c0..73a552914455 100644 --- a/drivers/iommu/intel-iommu-debugfs.c +++ b/drivers/iommu/intel-iommu-debugfs.c @@ -14,11 +14,15 @@ #include +#include "intel-pasid.h" + struct tbl_walk { u16 bus; u16 devfn; + u32 pasid; struct root_entry *rt_entry; struct context_entry *ctx_entry; + struct pasid_entry *pasid_tbl_entry; }; struct iommu_regset { @@ -142,21 +146,82 @@ static inline void print_tbl_walk(struct seq_file *m) { struct tbl_walk *tbl_wlk = m->private; - seq_printf(m, "%02x:%02x.%x\t0x%016llx:0x%016llx\t0x%016llx:0x%016llx\n", + seq_printf(m, "%02x:%02x.%x\t0x%016llx:0x%016llx\t0x%016llx:0x%016llx\t", tbl_wlk->bus, PCI_SLOT(tbl_wlk->devfn), PCI_FUNC(tbl_wlk->devfn), tbl_wlk->rt_entry->hi, tbl_wlk->rt_entry->lo, tbl_wlk->ctx_entry->hi, tbl_wlk->ctx_entry->lo); + + /* + * A legacy mode DMAR doesn't support PASID, hence default it to -1 + * indicating that it's invalid. Also, default all PASID related fields + * to 0. + */ + if (!tbl_wlk->pasid_tbl_entry) + seq_printf(m, "%-6d\t0x%016llx:0x%016llx:0x%016llx\n", -1, + (u64)0, (u64)0, (u64)0); + else + seq_printf(m, "%-6d\t0x%016llx:0x%016llx:0x%016llx\n", + tbl_wlk->pasid, tbl_wlk->pasid_tbl_entry->val[0], + tbl_wlk->pasid_tbl_entry->val[1], + tbl_wlk->pasid_tbl_entry->val[2]); +} + +static void pasid_tbl_walk(struct seq_file *m, struct pasid_entry *tbl_entry, + u16 dir_idx) +{ + struct tbl_walk *tbl_wlk = m->private; + u8 tbl_idx; + + for (tbl_idx = 0; tbl_idx < PASID_TBL_ENTRIES; tbl_idx++) { + if (pasid_pte_is_present(tbl_entry)) { + tbl_wlk->pasid_tbl_entry = tbl_entry; + tbl_wlk->pasid = (dir_idx << PASID_PDE_SHIFT) + tbl_idx; + print_tbl_walk(m); + } + + tbl_entry++; + } +} + +static void pasid_dir_walk(struct seq_file *m, u64 pasid_dir_ptr, + u16 pasid_dir_size) +{ + struct pasid_dir_entry *dir_entry = phys_to_virt(pasid_dir_ptr); + struct pasid_entry *pasid_tbl; + u16 dir_idx; + + for (dir_idx = 0; dir_idx < pasid_dir_size; dir_idx++) { + pasid_tbl = get_pasid_table_from_pde(dir_entry); + if (pasid_tbl) + pasid_tbl_walk(m, pasid_tbl, dir_idx); + + dir_entry++; + } } static void ctx_tbl_walk(struct seq_file *m, struct intel_iommu *iommu, u16 bus) { struct context_entry *context; - u16 devfn; + u16 devfn, pasid_dir_size; + u64 pasid_dir_ptr; for (devfn = 0; devfn < 256; devfn++) { struct tbl_walk tbl_wlk = {0}; + /* + * Scalable mode root entry points to upper scalable mode + * context table and lower scalable mode context table. Each + * scalable mode context table has 128 context entries where as + * legacy mode context table has 256 context entries. So in + * scalable mode, the context entries for former 128 devices are + * in the lower scalable mode context table, while the latter + * 128 devices are in the upper scalable mode context table. + * In scalable mode, when devfn > 127, iommu_context_addr() + * automatically refers to upper scalable mode context table and + * hence the caller doesn't have to worry about differences + * between scalable mode and non scalable mode. + */ context = iommu_context_addr(iommu, bus, devfn, 0); if (!context) return; @@ -170,6 +235,13 @@ static void ctx_tbl_walk(struct seq_file *m, struct intel_iommu *iommu, u16 bus) tbl_wlk.ctx_entry = context; m->private = &tbl_wlk; + if (pasid_supported(iommu) && is_pasid_enabled(context)) { + pasid_dir_ptr = context->lo & VTD_PAGE_MASK; + pasid_dir_size = get_pasid_dir_size(context); + pasid_dir_walk(m, pasid_dir_ptr, pasid_dir_size); + continue; + } + print_tbl_walk(m); } } @@ -182,7 +254,7 @@ static void root_tbl_walk(struct seq_file *m, struct intel_iommu *iommu) spin_lock_irqsave(&iommu->lock, flags); seq_printf(m, "IOMMU %s: Root Table Address: 0x%llx\n", iommu->name, (u64)virt_to_phys(iommu->root_entry)); - seq_puts(m, "B.D.F\tRoot_entry\t\t\t\tContext_entry\n"); + seq_puts(m, "B.D.F\tRoot_entry\t\t\t\tContext_entry\t\t\t\tPASID\tPASID_table_entry\n"); /* * No need to check if the root entry is present or not because -- 2.30.2