18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright © 2018 Intel Corporation.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors: Gayatri Kammela <gayatri.kammela@intel.com>
68c2ecf20Sopenharmony_ci *	    Sohil Mehta <sohil.mehta@intel.com>
78c2ecf20Sopenharmony_ci *	    Jacob Pan <jacob.jun.pan@linux.intel.com>
88c2ecf20Sopenharmony_ci *	    Lu Baolu <baolu.lu@linux.intel.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
128c2ecf20Sopenharmony_ci#include <linux/dmar.h>
138c2ecf20Sopenharmony_ci#include <linux/intel-iommu.h>
148c2ecf20Sopenharmony_ci#include <linux/pci.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <asm/irq_remapping.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "pasid.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct tbl_walk {
218c2ecf20Sopenharmony_ci	u16 bus;
228c2ecf20Sopenharmony_ci	u16 devfn;
238c2ecf20Sopenharmony_ci	u32 pasid;
248c2ecf20Sopenharmony_ci	struct root_entry *rt_entry;
258c2ecf20Sopenharmony_ci	struct context_entry *ctx_entry;
268c2ecf20Sopenharmony_ci	struct pasid_entry *pasid_tbl_entry;
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct iommu_regset {
308c2ecf20Sopenharmony_ci	int offset;
318c2ecf20Sopenharmony_ci	const char *regs;
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define IOMMU_REGSET_ENTRY(_reg_)					\
358c2ecf20Sopenharmony_ci	{ DMAR_##_reg_##_REG, __stringify(_reg_) }
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic const struct iommu_regset iommu_regs_32[] = {
388c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(VER),
398c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(GCMD),
408c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(GSTS),
418c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(FSTS),
428c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(FECTL),
438c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(FEDATA),
448c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(FEADDR),
458c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(FEUADDR),
468c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PMEN),
478c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PLMBASE),
488c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PLMLIMIT),
498c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(ICS),
508c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PRS),
518c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PECTL),
528c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PEDATA),
538c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PEADDR),
548c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PEUADDR),
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic const struct iommu_regset iommu_regs_64[] = {
588c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(CAP),
598c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(ECAP),
608c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(RTADDR),
618c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(CCMD),
628c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(AFLOG),
638c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PHMBASE),
648c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PHMLIMIT),
658c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(IQH),
668c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(IQT),
678c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(IQA),
688c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(IRTA),
698c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PQH),
708c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PQT),
718c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(PQA),
728c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRRCAP),
738c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRRDEF),
748c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_FIX64K_00000),
758c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_FIX16K_80000),
768c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_FIX16K_A0000),
778c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_FIX4K_C0000),
788c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_FIX4K_C8000),
798c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_FIX4K_D0000),
808c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_FIX4K_D8000),
818c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_FIX4K_E0000),
828c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_FIX4K_E8000),
838c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_FIX4K_F0000),
848c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_FIX4K_F8000),
858c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE0),
868c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK0),
878c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE1),
888c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK1),
898c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE2),
908c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK2),
918c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE3),
928c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK3),
938c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE4),
948c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK4),
958c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE5),
968c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK5),
978c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE6),
988c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK6),
998c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE7),
1008c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK7),
1018c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE8),
1028c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK8),
1038c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE9),
1048c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK9),
1058c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(VCCAP),
1068c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(VCMD),
1078c2ecf20Sopenharmony_ci	IOMMU_REGSET_ENTRY(VCRSP),
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int iommu_regset_show(struct seq_file *m, void *unused)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
1138c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
1148c2ecf20Sopenharmony_ci	unsigned long flag;
1158c2ecf20Sopenharmony_ci	int i, ret = 0;
1168c2ecf20Sopenharmony_ci	u64 value;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	rcu_read_lock();
1198c2ecf20Sopenharmony_ci	for_each_active_iommu(iommu, drhd) {
1208c2ecf20Sopenharmony_ci		if (!drhd->reg_base_addr) {
1218c2ecf20Sopenharmony_ci			seq_puts(m, "IOMMU: Invalid base address\n");
1228c2ecf20Sopenharmony_ci			ret = -EINVAL;
1238c2ecf20Sopenharmony_ci			goto out;
1248c2ecf20Sopenharmony_ci		}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		seq_printf(m, "IOMMU: %s Register Base Address: %llx\n",
1278c2ecf20Sopenharmony_ci			   iommu->name, drhd->reg_base_addr);
1288c2ecf20Sopenharmony_ci		seq_puts(m, "Name\t\t\tOffset\t\tContents\n");
1298c2ecf20Sopenharmony_ci		/*
1308c2ecf20Sopenharmony_ci		 * Publish the contents of the 64-bit hardware registers
1318c2ecf20Sopenharmony_ci		 * by adding the offset to the pointer (virtual address).
1328c2ecf20Sopenharmony_ci		 */
1338c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&iommu->register_lock, flag);
1348c2ecf20Sopenharmony_ci		for (i = 0 ; i < ARRAY_SIZE(iommu_regs_32); i++) {
1358c2ecf20Sopenharmony_ci			value = dmar_readl(iommu->reg + iommu_regs_32[i].offset);
1368c2ecf20Sopenharmony_ci			seq_printf(m, "%-16s\t0x%02x\t\t0x%016llx\n",
1378c2ecf20Sopenharmony_ci				   iommu_regs_32[i].regs, iommu_regs_32[i].offset,
1388c2ecf20Sopenharmony_ci				   value);
1398c2ecf20Sopenharmony_ci		}
1408c2ecf20Sopenharmony_ci		for (i = 0 ; i < ARRAY_SIZE(iommu_regs_64); i++) {
1418c2ecf20Sopenharmony_ci			value = dmar_readq(iommu->reg + iommu_regs_64[i].offset);
1428c2ecf20Sopenharmony_ci			seq_printf(m, "%-16s\t0x%02x\t\t0x%016llx\n",
1438c2ecf20Sopenharmony_ci				   iommu_regs_64[i].regs, iommu_regs_64[i].offset,
1448c2ecf20Sopenharmony_ci				   value);
1458c2ecf20Sopenharmony_ci		}
1468c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
1478c2ecf20Sopenharmony_ci		seq_putc(m, '\n');
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ciout:
1508c2ecf20Sopenharmony_ci	rcu_read_unlock();
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return ret;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(iommu_regset);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic inline void print_tbl_walk(struct seq_file *m)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct tbl_walk *tbl_wlk = m->private;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	seq_printf(m, "%02x:%02x.%x\t0x%016llx:0x%016llx\t0x%016llx:0x%016llx\t",
1618c2ecf20Sopenharmony_ci		   tbl_wlk->bus, PCI_SLOT(tbl_wlk->devfn),
1628c2ecf20Sopenharmony_ci		   PCI_FUNC(tbl_wlk->devfn), tbl_wlk->rt_entry->hi,
1638c2ecf20Sopenharmony_ci		   tbl_wlk->rt_entry->lo, tbl_wlk->ctx_entry->hi,
1648c2ecf20Sopenharmony_ci		   tbl_wlk->ctx_entry->lo);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/*
1678c2ecf20Sopenharmony_ci	 * A legacy mode DMAR doesn't support PASID, hence default it to -1
1688c2ecf20Sopenharmony_ci	 * indicating that it's invalid. Also, default all PASID related fields
1698c2ecf20Sopenharmony_ci	 * to 0.
1708c2ecf20Sopenharmony_ci	 */
1718c2ecf20Sopenharmony_ci	if (!tbl_wlk->pasid_tbl_entry)
1728c2ecf20Sopenharmony_ci		seq_printf(m, "%-6d\t0x%016llx:0x%016llx:0x%016llx\n", -1,
1738c2ecf20Sopenharmony_ci			   (u64)0, (u64)0, (u64)0);
1748c2ecf20Sopenharmony_ci	else
1758c2ecf20Sopenharmony_ci		seq_printf(m, "%-6d\t0x%016llx:0x%016llx:0x%016llx\n",
1768c2ecf20Sopenharmony_ci			   tbl_wlk->pasid, tbl_wlk->pasid_tbl_entry->val[2],
1778c2ecf20Sopenharmony_ci			   tbl_wlk->pasid_tbl_entry->val[1],
1788c2ecf20Sopenharmony_ci			   tbl_wlk->pasid_tbl_entry->val[0]);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic void pasid_tbl_walk(struct seq_file *m, struct pasid_entry *tbl_entry,
1828c2ecf20Sopenharmony_ci			   u16 dir_idx)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct tbl_walk *tbl_wlk = m->private;
1858c2ecf20Sopenharmony_ci	u8 tbl_idx;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	for (tbl_idx = 0; tbl_idx < PASID_TBL_ENTRIES; tbl_idx++) {
1888c2ecf20Sopenharmony_ci		if (pasid_pte_is_present(tbl_entry)) {
1898c2ecf20Sopenharmony_ci			tbl_wlk->pasid_tbl_entry = tbl_entry;
1908c2ecf20Sopenharmony_ci			tbl_wlk->pasid = (dir_idx << PASID_PDE_SHIFT) + tbl_idx;
1918c2ecf20Sopenharmony_ci			print_tbl_walk(m);
1928c2ecf20Sopenharmony_ci		}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		tbl_entry++;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void pasid_dir_walk(struct seq_file *m, u64 pasid_dir_ptr,
1998c2ecf20Sopenharmony_ci			   u16 pasid_dir_size)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct pasid_dir_entry *dir_entry = phys_to_virt(pasid_dir_ptr);
2028c2ecf20Sopenharmony_ci	struct pasid_entry *pasid_tbl;
2038c2ecf20Sopenharmony_ci	u16 dir_idx;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	for (dir_idx = 0; dir_idx < pasid_dir_size; dir_idx++) {
2068c2ecf20Sopenharmony_ci		pasid_tbl = get_pasid_table_from_pde(dir_entry);
2078c2ecf20Sopenharmony_ci		if (pasid_tbl)
2088c2ecf20Sopenharmony_ci			pasid_tbl_walk(m, pasid_tbl, dir_idx);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		dir_entry++;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic void ctx_tbl_walk(struct seq_file *m, struct intel_iommu *iommu, u16 bus)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct context_entry *context;
2178c2ecf20Sopenharmony_ci	u16 devfn, pasid_dir_size;
2188c2ecf20Sopenharmony_ci	u64 pasid_dir_ptr;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	for (devfn = 0; devfn < 256; devfn++) {
2218c2ecf20Sopenharmony_ci		struct tbl_walk tbl_wlk = {0};
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		/*
2248c2ecf20Sopenharmony_ci		 * Scalable mode root entry points to upper scalable mode
2258c2ecf20Sopenharmony_ci		 * context table and lower scalable mode context table. Each
2268c2ecf20Sopenharmony_ci		 * scalable mode context table has 128 context entries where as
2278c2ecf20Sopenharmony_ci		 * legacy mode context table has 256 context entries. So in
2288c2ecf20Sopenharmony_ci		 * scalable mode, the context entries for former 128 devices are
2298c2ecf20Sopenharmony_ci		 * in the lower scalable mode context table, while the latter
2308c2ecf20Sopenharmony_ci		 * 128 devices are in the upper scalable mode context table.
2318c2ecf20Sopenharmony_ci		 * In scalable mode, when devfn > 127, iommu_context_addr()
2328c2ecf20Sopenharmony_ci		 * automatically refers to upper scalable mode context table and
2338c2ecf20Sopenharmony_ci		 * hence the caller doesn't have to worry about differences
2348c2ecf20Sopenharmony_ci		 * between scalable mode and non scalable mode.
2358c2ecf20Sopenharmony_ci		 */
2368c2ecf20Sopenharmony_ci		context = iommu_context_addr(iommu, bus, devfn, 0);
2378c2ecf20Sopenharmony_ci		if (!context)
2388c2ecf20Sopenharmony_ci			return;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		if (!context_present(context))
2418c2ecf20Sopenharmony_ci			continue;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		tbl_wlk.bus = bus;
2448c2ecf20Sopenharmony_ci		tbl_wlk.devfn = devfn;
2458c2ecf20Sopenharmony_ci		tbl_wlk.rt_entry = &iommu->root_entry[bus];
2468c2ecf20Sopenharmony_ci		tbl_wlk.ctx_entry = context;
2478c2ecf20Sopenharmony_ci		m->private = &tbl_wlk;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		if (dmar_readq(iommu->reg + DMAR_RTADDR_REG) & DMA_RTADDR_SMT) {
2508c2ecf20Sopenharmony_ci			pasid_dir_ptr = context->lo & VTD_PAGE_MASK;
2518c2ecf20Sopenharmony_ci			pasid_dir_size = get_pasid_dir_size(context);
2528c2ecf20Sopenharmony_ci			pasid_dir_walk(m, pasid_dir_ptr, pasid_dir_size);
2538c2ecf20Sopenharmony_ci			continue;
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		print_tbl_walk(m);
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic void root_tbl_walk(struct seq_file *m, struct intel_iommu *iommu)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	unsigned long flags;
2638c2ecf20Sopenharmony_ci	u16 bus;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	spin_lock_irqsave(&iommu->lock, flags);
2668c2ecf20Sopenharmony_ci	seq_printf(m, "IOMMU %s: Root Table Address: 0x%llx\n", iommu->name,
2678c2ecf20Sopenharmony_ci		   (u64)virt_to_phys(iommu->root_entry));
2688c2ecf20Sopenharmony_ci	seq_puts(m, "B.D.F\tRoot_entry\t\t\t\tContext_entry\t\t\t\tPASID\tPASID_table_entry\n");
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/*
2718c2ecf20Sopenharmony_ci	 * No need to check if the root entry is present or not because
2728c2ecf20Sopenharmony_ci	 * iommu_context_addr() performs the same check before returning
2738c2ecf20Sopenharmony_ci	 * context entry.
2748c2ecf20Sopenharmony_ci	 */
2758c2ecf20Sopenharmony_ci	for (bus = 0; bus < 256; bus++)
2768c2ecf20Sopenharmony_ci		ctx_tbl_walk(m, iommu, bus);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&iommu->lock, flags);
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic int dmar_translation_struct_show(struct seq_file *m, void *unused)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
2848c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
2858c2ecf20Sopenharmony_ci	u32 sts;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	rcu_read_lock();
2888c2ecf20Sopenharmony_ci	for_each_active_iommu(iommu, drhd) {
2898c2ecf20Sopenharmony_ci		sts = dmar_readl(iommu->reg + DMAR_GSTS_REG);
2908c2ecf20Sopenharmony_ci		if (!(sts & DMA_GSTS_TES)) {
2918c2ecf20Sopenharmony_ci			seq_printf(m, "DMA Remapping is not enabled on %s\n",
2928c2ecf20Sopenharmony_ci				   iommu->name);
2938c2ecf20Sopenharmony_ci			continue;
2948c2ecf20Sopenharmony_ci		}
2958c2ecf20Sopenharmony_ci		root_tbl_walk(m, iommu);
2968c2ecf20Sopenharmony_ci		seq_putc(m, '\n');
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci	rcu_read_unlock();
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	return 0;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(dmar_translation_struct);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic inline unsigned long level_to_directory_size(int level)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	return BIT_ULL(VTD_PAGE_SHIFT + VTD_STRIDE_SHIFT * (level - 1));
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic inline void
3108c2ecf20Sopenharmony_cidump_page_info(struct seq_file *m, unsigned long iova, u64 *path)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	seq_printf(m, "0x%013lx |\t0x%016llx\t0x%016llx\t0x%016llx\t0x%016llx\t0x%016llx\n",
3138c2ecf20Sopenharmony_ci		   iova >> VTD_PAGE_SHIFT, path[5], path[4],
3148c2ecf20Sopenharmony_ci		   path[3], path[2], path[1]);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic void pgtable_walk_level(struct seq_file *m, struct dma_pte *pde,
3188c2ecf20Sopenharmony_ci			       int level, unsigned long start,
3198c2ecf20Sopenharmony_ci			       u64 *path)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	int i;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (level > 5 || level < 1)
3248c2ecf20Sopenharmony_ci		return;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	for (i = 0; i < BIT_ULL(VTD_STRIDE_SHIFT);
3278c2ecf20Sopenharmony_ci			i++, pde++, start += level_to_directory_size(level)) {
3288c2ecf20Sopenharmony_ci		if (!dma_pte_present(pde))
3298c2ecf20Sopenharmony_ci			continue;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		path[level] = pde->val;
3328c2ecf20Sopenharmony_ci		if (dma_pte_superpage(pde) || level == 1)
3338c2ecf20Sopenharmony_ci			dump_page_info(m, start, path);
3348c2ecf20Sopenharmony_ci		else
3358c2ecf20Sopenharmony_ci			pgtable_walk_level(m, phys_to_virt(dma_pte_addr(pde)),
3368c2ecf20Sopenharmony_ci					   level - 1, start, path);
3378c2ecf20Sopenharmony_ci		path[level] = 0;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int show_device_domain_translation(struct device *dev, void *data)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct dmar_domain *domain = find_domain(dev);
3448c2ecf20Sopenharmony_ci	struct seq_file *m = data;
3458c2ecf20Sopenharmony_ci	u64 path[6] = { 0 };
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!domain)
3488c2ecf20Sopenharmony_ci		return 0;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	seq_printf(m, "Device %s with pasid %d @0x%llx\n",
3518c2ecf20Sopenharmony_ci		   dev_name(dev), domain->default_pasid,
3528c2ecf20Sopenharmony_ci		   (u64)virt_to_phys(domain->pgd));
3538c2ecf20Sopenharmony_ci	seq_puts(m, "IOVA_PFN\t\tPML5E\t\t\tPML4E\t\t\tPDPE\t\t\tPDE\t\t\tPTE\n");
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	pgtable_walk_level(m, domain->pgd, domain->agaw + 2, 0, path);
3568c2ecf20Sopenharmony_ci	seq_putc(m, '\n');
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return 0;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int domain_translation_struct_show(struct seq_file *m, void *unused)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	unsigned long flags;
3648c2ecf20Sopenharmony_ci	int ret;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&device_domain_lock, flags);
3678c2ecf20Sopenharmony_ci	ret = bus_for_each_dev(&pci_bus_type, NULL, m,
3688c2ecf20Sopenharmony_ci			       show_device_domain_translation);
3698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&device_domain_lock, flags);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	return ret;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(domain_translation_struct);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic void invalidation_queue_entry_show(struct seq_file *m,
3768c2ecf20Sopenharmony_ci					  struct intel_iommu *iommu)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	int index, shift = qi_shift(iommu);
3798c2ecf20Sopenharmony_ci	struct qi_desc *desc;
3808c2ecf20Sopenharmony_ci	int offset;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (ecap_smts(iommu->ecap))
3838c2ecf20Sopenharmony_ci		seq_puts(m, "Index\t\tqw0\t\t\tqw1\t\t\tqw2\t\t\tqw3\t\t\tstatus\n");
3848c2ecf20Sopenharmony_ci	else
3858c2ecf20Sopenharmony_ci		seq_puts(m, "Index\t\tqw0\t\t\tqw1\t\t\tstatus\n");
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	for (index = 0; index < QI_LENGTH; index++) {
3888c2ecf20Sopenharmony_ci		offset = index << shift;
3898c2ecf20Sopenharmony_ci		desc = iommu->qi->desc + offset;
3908c2ecf20Sopenharmony_ci		if (ecap_smts(iommu->ecap))
3918c2ecf20Sopenharmony_ci			seq_printf(m, "%5d\t%016llx\t%016llx\t%016llx\t%016llx\t%016x\n",
3928c2ecf20Sopenharmony_ci				   index, desc->qw0, desc->qw1,
3938c2ecf20Sopenharmony_ci				   desc->qw2, desc->qw3,
3948c2ecf20Sopenharmony_ci				   iommu->qi->desc_status[index]);
3958c2ecf20Sopenharmony_ci		else
3968c2ecf20Sopenharmony_ci			seq_printf(m, "%5d\t%016llx\t%016llx\t%016x\n",
3978c2ecf20Sopenharmony_ci				   index, desc->qw0, desc->qw1,
3988c2ecf20Sopenharmony_ci				   iommu->qi->desc_status[index]);
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic int invalidation_queue_show(struct seq_file *m, void *unused)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
4058c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
4068c2ecf20Sopenharmony_ci	unsigned long flags;
4078c2ecf20Sopenharmony_ci	struct q_inval *qi;
4088c2ecf20Sopenharmony_ci	int shift;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	rcu_read_lock();
4118c2ecf20Sopenharmony_ci	for_each_active_iommu(iommu, drhd) {
4128c2ecf20Sopenharmony_ci		qi = iommu->qi;
4138c2ecf20Sopenharmony_ci		shift = qi_shift(iommu);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		if (!qi || !ecap_qis(iommu->ecap))
4168c2ecf20Sopenharmony_ci			continue;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		seq_printf(m, "Invalidation queue on IOMMU: %s\n", iommu->name);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&qi->q_lock, flags);
4218c2ecf20Sopenharmony_ci		seq_printf(m, " Base: 0x%llx\tHead: %lld\tTail: %lld\n",
4228c2ecf20Sopenharmony_ci			   (u64)virt_to_phys(qi->desc),
4238c2ecf20Sopenharmony_ci			   dmar_readq(iommu->reg + DMAR_IQH_REG) >> shift,
4248c2ecf20Sopenharmony_ci			   dmar_readq(iommu->reg + DMAR_IQT_REG) >> shift);
4258c2ecf20Sopenharmony_ci		invalidation_queue_entry_show(m, iommu);
4268c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&qi->q_lock, flags);
4278c2ecf20Sopenharmony_ci		seq_putc(m, '\n');
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci	rcu_read_unlock();
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return 0;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(invalidation_queue);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci#ifdef CONFIG_IRQ_REMAP
4368c2ecf20Sopenharmony_cistatic void ir_tbl_remap_entry_show(struct seq_file *m,
4378c2ecf20Sopenharmony_ci				    struct intel_iommu *iommu)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct irte *ri_entry;
4408c2ecf20Sopenharmony_ci	unsigned long flags;
4418c2ecf20Sopenharmony_ci	int idx;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	seq_puts(m, " Entry SrcID   DstID    Vct IRTE_high\t\tIRTE_low\n");
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
4468c2ecf20Sopenharmony_ci	for (idx = 0; idx < INTR_REMAP_TABLE_ENTRIES; idx++) {
4478c2ecf20Sopenharmony_ci		ri_entry = &iommu->ir_table->base[idx];
4488c2ecf20Sopenharmony_ci		if (!ri_entry->present || ri_entry->p_pst)
4498c2ecf20Sopenharmony_ci			continue;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci		seq_printf(m, " %-5d %02x:%02x.%01x %08x %02x  %016llx\t%016llx\n",
4528c2ecf20Sopenharmony_ci			   idx, PCI_BUS_NUM(ri_entry->sid),
4538c2ecf20Sopenharmony_ci			   PCI_SLOT(ri_entry->sid), PCI_FUNC(ri_entry->sid),
4548c2ecf20Sopenharmony_ci			   ri_entry->dest_id, ri_entry->vector,
4558c2ecf20Sopenharmony_ci			   ri_entry->high, ri_entry->low);
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic void ir_tbl_posted_entry_show(struct seq_file *m,
4618c2ecf20Sopenharmony_ci				     struct intel_iommu *iommu)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	struct irte *pi_entry;
4648c2ecf20Sopenharmony_ci	unsigned long flags;
4658c2ecf20Sopenharmony_ci	int idx;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	seq_puts(m, " Entry SrcID   PDA_high PDA_low  Vct IRTE_high\t\tIRTE_low\n");
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
4708c2ecf20Sopenharmony_ci	for (idx = 0; idx < INTR_REMAP_TABLE_ENTRIES; idx++) {
4718c2ecf20Sopenharmony_ci		pi_entry = &iommu->ir_table->base[idx];
4728c2ecf20Sopenharmony_ci		if (!pi_entry->present || !pi_entry->p_pst)
4738c2ecf20Sopenharmony_ci			continue;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		seq_printf(m, " %-5d %02x:%02x.%01x %08x %08x %02x  %016llx\t%016llx\n",
4768c2ecf20Sopenharmony_ci			   idx, PCI_BUS_NUM(pi_entry->sid),
4778c2ecf20Sopenharmony_ci			   PCI_SLOT(pi_entry->sid), PCI_FUNC(pi_entry->sid),
4788c2ecf20Sopenharmony_ci			   pi_entry->pda_h, pi_entry->pda_l << 6,
4798c2ecf20Sopenharmony_ci			   pi_entry->vector, pi_entry->high,
4808c2ecf20Sopenharmony_ci			   pi_entry->low);
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci/*
4868c2ecf20Sopenharmony_ci * For active IOMMUs go through the Interrupt remapping
4878c2ecf20Sopenharmony_ci * table and print valid entries in a table format for
4888c2ecf20Sopenharmony_ci * Remapped and Posted Interrupts.
4898c2ecf20Sopenharmony_ci */
4908c2ecf20Sopenharmony_cistatic int ir_translation_struct_show(struct seq_file *m, void *unused)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
4938c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
4948c2ecf20Sopenharmony_ci	u64 irta;
4958c2ecf20Sopenharmony_ci	u32 sts;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	rcu_read_lock();
4988c2ecf20Sopenharmony_ci	for_each_active_iommu(iommu, drhd) {
4998c2ecf20Sopenharmony_ci		if (!ecap_ir_support(iommu->ecap))
5008c2ecf20Sopenharmony_ci			continue;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		seq_printf(m, "Remapped Interrupt supported on IOMMU: %s\n",
5038c2ecf20Sopenharmony_ci			   iommu->name);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci		sts = dmar_readl(iommu->reg + DMAR_GSTS_REG);
5068c2ecf20Sopenharmony_ci		if (iommu->ir_table && (sts & DMA_GSTS_IRES)) {
5078c2ecf20Sopenharmony_ci			irta = virt_to_phys(iommu->ir_table->base);
5088c2ecf20Sopenharmony_ci			seq_printf(m, " IR table address:%llx\n", irta);
5098c2ecf20Sopenharmony_ci			ir_tbl_remap_entry_show(m, iommu);
5108c2ecf20Sopenharmony_ci		} else {
5118c2ecf20Sopenharmony_ci			seq_puts(m, "Interrupt Remapping is not enabled\n");
5128c2ecf20Sopenharmony_ci		}
5138c2ecf20Sopenharmony_ci		seq_putc(m, '\n');
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	seq_puts(m, "****\n\n");
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	for_each_active_iommu(iommu, drhd) {
5198c2ecf20Sopenharmony_ci		if (!cap_pi_support(iommu->cap))
5208c2ecf20Sopenharmony_ci			continue;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci		seq_printf(m, "Posted Interrupt supported on IOMMU: %s\n",
5238c2ecf20Sopenharmony_ci			   iommu->name);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		if (iommu->ir_table) {
5268c2ecf20Sopenharmony_ci			irta = virt_to_phys(iommu->ir_table->base);
5278c2ecf20Sopenharmony_ci			seq_printf(m, " IR table address:%llx\n", irta);
5288c2ecf20Sopenharmony_ci			ir_tbl_posted_entry_show(m, iommu);
5298c2ecf20Sopenharmony_ci		} else {
5308c2ecf20Sopenharmony_ci			seq_puts(m, "Interrupt Remapping is not enabled\n");
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci		seq_putc(m, '\n');
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci	rcu_read_unlock();
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	return 0;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ir_translation_struct);
5398c2ecf20Sopenharmony_ci#endif
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_civoid __init intel_iommu_debugfs_init(void)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	struct dentry *intel_iommu_debug = debugfs_create_dir("intel",
5448c2ecf20Sopenharmony_ci						iommu_debugfs_dir);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	debugfs_create_file("iommu_regset", 0444, intel_iommu_debug, NULL,
5478c2ecf20Sopenharmony_ci			    &iommu_regset_fops);
5488c2ecf20Sopenharmony_ci	debugfs_create_file("dmar_translation_struct", 0444, intel_iommu_debug,
5498c2ecf20Sopenharmony_ci			    NULL, &dmar_translation_struct_fops);
5508c2ecf20Sopenharmony_ci	debugfs_create_file("domain_translation_struct", 0444,
5518c2ecf20Sopenharmony_ci			    intel_iommu_debug, NULL,
5528c2ecf20Sopenharmony_ci			    &domain_translation_struct_fops);
5538c2ecf20Sopenharmony_ci	debugfs_create_file("invalidation_queue", 0444, intel_iommu_debug,
5548c2ecf20Sopenharmony_ci			    NULL, &invalidation_queue_fops);
5558c2ecf20Sopenharmony_ci#ifdef CONFIG_IRQ_REMAP
5568c2ecf20Sopenharmony_ci	debugfs_create_file("ir_translation_struct", 0444, intel_iommu_debug,
5578c2ecf20Sopenharmony_ci			    NULL, &ir_translation_struct_fops);
5588c2ecf20Sopenharmony_ci#endif
5598c2ecf20Sopenharmony_ci}
560