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