Skip to content

Commit 2b437e8

Browse files
nuttyliujoergroedel
authored andcommitted
iommu/vt-d: debugfs: Support dumping a specified page table
The original debugfs only dumps all page tables without pasid. With pasid supported, the page table with pasid also needs to be dumped. This patch supports dumping a specified page table in legacy mode or scalable mode with or without a specified pasid. For legacy mode, according to bus number and DEVFN, traverse the root table and context table to get the pointer of page table in the context table entry, then dump the specified page table. For scalable mode, according to bus number, DEVFN and pasid, traverse the root table, context table, pasid directory and pasid table to get the pointer of page table in the pasid table entry, then dump the specified page table.. Examples are as follows: 1) Dump the page table of device "0000:00:1f.0" that only supports legacy mode. $ sudo cat /sys/kernel/debug/iommu/intel/0000:00:1f.0/domain_translation_struct 2) Dump the page table of device "0000:00:0a.0" with PASID "1" that supports scalable mode. $ sudo cat /sys/kernel/debug/iommu/intel/0000:00:0a.0/1/domain_translation_struct Suggested-by: Kevin Tian <[email protected]> Signed-off-by: Jingqi Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Lu Baolu <[email protected]> Signed-off-by: Joerg Roedel <[email protected]>
1 parent d87731f commit 2b437e8

File tree

1 file changed

+119
-34
lines changed

1 file changed

+119
-34
lines changed

drivers/iommu/intel/debugfs.c

Lines changed: 119 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -347,58 +347,140 @@ static void pgtable_walk_level(struct seq_file *m, struct dma_pte *pde,
347347
}
348348
}
349349

350-
static int __show_device_domain_translation(struct device *dev, void *data)
350+
static int domain_translation_struct_show(struct seq_file *m,
351+
struct device_domain_info *info,
352+
ioasid_t pasid)
351353
{
352-
struct dmar_domain *domain;
353-
struct seq_file *m = data;
354-
u64 path[6] = { 0 };
355-
356-
domain = to_dmar_domain(iommu_get_domain_for_dev(dev));
357-
if (!domain)
358-
return 0;
354+
bool scalable, found = false;
355+
struct dmar_drhd_unit *drhd;
356+
struct intel_iommu *iommu;
357+
u16 devfn, bus, seg;
359358

360-
seq_printf(m, "Device %s @0x%llx\n", dev_name(dev),
361-
(u64)virt_to_phys(domain->pgd));
362-
seq_puts(m, "IOVA_PFN\t\tPML5E\t\t\tPML4E\t\t\tPDPE\t\t\tPDE\t\t\tPTE\n");
359+
bus = info->bus;
360+
devfn = info->devfn;
361+
seg = info->segment;
363362

364-
pgtable_walk_level(m, domain->pgd, domain->agaw + 2, 0, path);
365-
seq_putc(m, '\n');
363+
rcu_read_lock();
364+
for_each_active_iommu(iommu, drhd) {
365+
struct context_entry *context;
366+
u64 pgd, path[6] = { 0 };
367+
u32 sts, agaw;
366368

367-
/* Don't iterate */
368-
return 1;
369-
}
369+
if (seg != iommu->segment)
370+
continue;
370371

371-
static int show_device_domain_translation(struct device *dev, void *data)
372-
{
373-
struct iommu_group *group;
372+
sts = dmar_readl(iommu->reg + DMAR_GSTS_REG);
373+
if (!(sts & DMA_GSTS_TES)) {
374+
seq_printf(m, "DMA Remapping is not enabled on %s\n",
375+
iommu->name);
376+
continue;
377+
}
378+
if (dmar_readq(iommu->reg + DMAR_RTADDR_REG) & DMA_RTADDR_SMT)
379+
scalable = true;
380+
else
381+
scalable = false;
374382

375-
group = iommu_group_get(dev);
376-
if (group) {
377383
/*
378-
* The group->mutex is held across the callback, which will
379-
* block calls to iommu_attach/detach_group/device. Hence,
384+
* The iommu->lock is held across the callback, which will
385+
* block calls to domain_attach/domain_detach. Hence,
380386
* the domain of the device will not change during traversal.
381387
*
382-
* All devices in an iommu group share a single domain, hence
383-
* we only dump the domain of the first device. Even though,
384-
* this code still possibly races with the iommu_unmap()
388+
* Traversing page table possibly races with the iommu_unmap()
385389
* interface. This could be solved by RCU-freeing the page
386390
* table pages in the iommu_unmap() path.
387391
*/
388-
iommu_group_for_each_dev(group, data,
389-
__show_device_domain_translation);
390-
iommu_group_put(group);
392+
spin_lock(&iommu->lock);
393+
394+
context = iommu_context_addr(iommu, bus, devfn, 0);
395+
if (!context || !context_present(context))
396+
goto iommu_unlock;
397+
398+
if (scalable) { /* scalable mode */
399+
struct pasid_entry *pasid_tbl, *pasid_tbl_entry;
400+
struct pasid_dir_entry *dir_tbl, *dir_entry;
401+
u16 dir_idx, tbl_idx, pgtt;
402+
u64 pasid_dir_ptr;
403+
404+
pasid_dir_ptr = context->lo & VTD_PAGE_MASK;
405+
406+
/* Dump specified device domain mappings with PASID. */
407+
dir_idx = pasid >> PASID_PDE_SHIFT;
408+
tbl_idx = pasid & PASID_PTE_MASK;
409+
410+
dir_tbl = phys_to_virt(pasid_dir_ptr);
411+
dir_entry = &dir_tbl[dir_idx];
412+
413+
pasid_tbl = get_pasid_table_from_pde(dir_entry);
414+
if (!pasid_tbl)
415+
goto iommu_unlock;
416+
417+
pasid_tbl_entry = &pasid_tbl[tbl_idx];
418+
if (!pasid_pte_is_present(pasid_tbl_entry))
419+
goto iommu_unlock;
420+
421+
/*
422+
* According to PASID Granular Translation Type(PGTT),
423+
* get the page table pointer.
424+
*/
425+
pgtt = (u16)(pasid_tbl_entry->val[0] & GENMASK_ULL(8, 6)) >> 6;
426+
agaw = (u8)(pasid_tbl_entry->val[0] & GENMASK_ULL(4, 2)) >> 2;
427+
428+
switch (pgtt) {
429+
case PASID_ENTRY_PGTT_FL_ONLY:
430+
pgd = pasid_tbl_entry->val[2];
431+
break;
432+
case PASID_ENTRY_PGTT_SL_ONLY:
433+
case PASID_ENTRY_PGTT_NESTED:
434+
pgd = pasid_tbl_entry->val[0];
435+
break;
436+
default:
437+
goto iommu_unlock;
438+
}
439+
pgd &= VTD_PAGE_MASK;
440+
} else { /* legacy mode */
441+
pgd = context->lo & VTD_PAGE_MASK;
442+
agaw = context->hi & 7;
443+
}
444+
445+
seq_printf(m, "Device %04x:%02x:%02x.%x ",
446+
iommu->segment, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
447+
448+
if (scalable)
449+
seq_printf(m, "with pasid %x @0x%llx\n", pasid, pgd);
450+
else
451+
seq_printf(m, "@0x%llx\n", pgd);
452+
453+
seq_printf(m, "%-17s\t%-18s\t%-18s\t%-18s\t%-18s\t%-s\n",
454+
"IOVA_PFN", "PML5E", "PML4E", "PDPE", "PDE", "PTE");
455+
pgtable_walk_level(m, phys_to_virt(pgd), agaw + 2, 0, path);
456+
457+
found = true;
458+
iommu_unlock:
459+
spin_unlock(&iommu->lock);
460+
if (found)
461+
break;
391462
}
463+
rcu_read_unlock();
392464

393465
return 0;
394466
}
395467

396-
static int domain_translation_struct_show(struct seq_file *m, void *unused)
468+
static int dev_domain_translation_struct_show(struct seq_file *m, void *unused)
397469
{
398-
return bus_for_each_dev(&pci_bus_type, NULL, m,
399-
show_device_domain_translation);
470+
struct device_domain_info *info = (struct device_domain_info *)m->private;
471+
472+
return domain_translation_struct_show(m, info, IOMMU_NO_PASID);
400473
}
401-
DEFINE_SHOW_ATTRIBUTE(domain_translation_struct);
474+
DEFINE_SHOW_ATTRIBUTE(dev_domain_translation_struct);
475+
476+
static int pasid_domain_translation_struct_show(struct seq_file *m, void *unused)
477+
{
478+
struct dev_pasid_info *dev_pasid = (struct dev_pasid_info *)m->private;
479+
struct device_domain_info *info = dev_iommu_priv_get(dev_pasid->dev);
480+
481+
return domain_translation_struct_show(m, info, dev_pasid->pasid);
482+
}
483+
DEFINE_SHOW_ATTRIBUTE(pasid_domain_translation_struct);
402484

403485
static void invalidation_queue_entry_show(struct seq_file *m,
404486
struct intel_iommu *iommu)
@@ -700,7 +782,7 @@ void intel_iommu_debugfs_create_dev(struct device_domain_info *info)
700782
info->debugfs_dentry = debugfs_create_dir(dev_name(info->dev), intel_iommu_debug);
701783

702784
debugfs_create_file("domain_translation_struct", 0444, info->debugfs_dentry,
703-
NULL, &domain_translation_struct_fops);
785+
info, &dev_domain_translation_struct_fops);
704786
}
705787

706788
/* Remove the device debugfs directory. */
@@ -726,6 +808,9 @@ void intel_iommu_debugfs_create_dev_pasid(struct dev_pasid_info *dev_pasid)
726808

727809
sprintf(dir_name, "%x", dev_pasid->pasid);
728810
dev_pasid->debugfs_dentry = debugfs_create_dir(dir_name, info->debugfs_dentry);
811+
812+
debugfs_create_file("domain_translation_struct", 0444, dev_pasid->debugfs_dentry,
813+
dev_pasid, &pasid_domain_translation_struct_fops);
729814
}
730815

731816
/* Remove the device pasid debugfs directory. */

0 commit comments

Comments
 (0)