807 lines · 22.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright © 2018 Intel Corporation. * * Authors: Gayatri Kammela <gayatri.kammela@intel.com> *	    Sohil Mehta <sohil.mehta@intel.com> *	    Jacob Pan <jacob.jun.pan@linux.intel.com> *	    Lu Baolu <baolu.lu@linux.intel.com> */ #include <linux/debugfs.h>#include <linux/dmar.h>#include <linux/pci.h> #include <asm/irq_remapping.h> #include "iommu.h"#include "pasid.h"#include "perf.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 {	int offset;	const char *regs;}; #define DEBUG_BUFFER_SIZE	1024static char debug_buf[DEBUG_BUFFER_SIZE]; #define IOMMU_REGSET_ENTRY(_reg_)					\	{ DMAR_##_reg_##_REG, __stringify(_reg_) } static const struct iommu_regset iommu_regs_32[] = {	IOMMU_REGSET_ENTRY(VER),	IOMMU_REGSET_ENTRY(GCMD),	IOMMU_REGSET_ENTRY(GSTS),	IOMMU_REGSET_ENTRY(FSTS),	IOMMU_REGSET_ENTRY(FECTL),	IOMMU_REGSET_ENTRY(FEDATA),	IOMMU_REGSET_ENTRY(FEADDR),	IOMMU_REGSET_ENTRY(FEUADDR),	IOMMU_REGSET_ENTRY(PMEN),	IOMMU_REGSET_ENTRY(PLMBASE),	IOMMU_REGSET_ENTRY(PLMLIMIT),	IOMMU_REGSET_ENTRY(ICS),	IOMMU_REGSET_ENTRY(PRS),	IOMMU_REGSET_ENTRY(PECTL),	IOMMU_REGSET_ENTRY(PEDATA),	IOMMU_REGSET_ENTRY(PEADDR),	IOMMU_REGSET_ENTRY(PEUADDR),}; static const struct iommu_regset iommu_regs_64[] = {	IOMMU_REGSET_ENTRY(CAP),	IOMMU_REGSET_ENTRY(ECAP),	IOMMU_REGSET_ENTRY(RTADDR),	IOMMU_REGSET_ENTRY(PHMBASE),	IOMMU_REGSET_ENTRY(PHMLIMIT),	IOMMU_REGSET_ENTRY(IQH),	IOMMU_REGSET_ENTRY(IQT),	IOMMU_REGSET_ENTRY(IQA),	IOMMU_REGSET_ENTRY(IRTA),	IOMMU_REGSET_ENTRY(PQH),	IOMMU_REGSET_ENTRY(PQT),	IOMMU_REGSET_ENTRY(PQA),	IOMMU_REGSET_ENTRY(MTRRCAP),	IOMMU_REGSET_ENTRY(MTRRDEF),	IOMMU_REGSET_ENTRY(MTRR_FIX64K_00000),	IOMMU_REGSET_ENTRY(MTRR_FIX16K_80000),	IOMMU_REGSET_ENTRY(MTRR_FIX16K_A0000),	IOMMU_REGSET_ENTRY(MTRR_FIX4K_C0000),	IOMMU_REGSET_ENTRY(MTRR_FIX4K_C8000),	IOMMU_REGSET_ENTRY(MTRR_FIX4K_D0000),	IOMMU_REGSET_ENTRY(MTRR_FIX4K_D8000),	IOMMU_REGSET_ENTRY(MTRR_FIX4K_E0000),	IOMMU_REGSET_ENTRY(MTRR_FIX4K_E8000),	IOMMU_REGSET_ENTRY(MTRR_FIX4K_F0000),	IOMMU_REGSET_ENTRY(MTRR_FIX4K_F8000),	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE0),	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK0),	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE1),	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK1),	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE2),	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK2),	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE3),	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK3),	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE4),	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK4),	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE5),	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK5),	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE6),	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK6),	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE7),	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK7),	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE8),	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK8),	IOMMU_REGSET_ENTRY(MTRR_PHYSBASE9),	IOMMU_REGSET_ENTRY(MTRR_PHYSMASK9),}; static struct dentry *intel_iommu_debug; static int iommu_regset_show(struct seq_file *m, void *unused){	struct dmar_drhd_unit *drhd;	struct intel_iommu *iommu;	unsigned long flag;	int i, ret = 0;	u64 value; 	rcu_read_lock();	for_each_active_iommu(iommu, drhd) {		if (!drhd->reg_base_addr) {			seq_puts(m, "IOMMU: Invalid base address\n");			ret = -EINVAL;			goto out;		} 		seq_printf(m, "IOMMU: %s Register Base Address: %llx\n",			   iommu->name, drhd->reg_base_addr);		seq_puts(m, "Name\t\t\tOffset\t\tContents\n");		/*		 * Publish the contents of the 64-bit hardware registers		 * by adding the offset to the pointer (virtual address).		 */		raw_spin_lock_irqsave(&iommu->register_lock, flag);		for (i = 0 ; i < ARRAY_SIZE(iommu_regs_32); i++) {			value = readl(iommu->reg + iommu_regs_32[i].offset);			seq_printf(m, "%-16s\t0x%02x\t\t0x%016llx\n",				   iommu_regs_32[i].regs, iommu_regs_32[i].offset,				   value);		}		for (i = 0 ; i < ARRAY_SIZE(iommu_regs_64); i++) {			value = readq(iommu->reg + iommu_regs_64[i].offset);			seq_printf(m, "%-16s\t0x%02x\t\t0x%016llx\n",				   iommu_regs_64[i].regs, iommu_regs_64[i].offset,				   value);		}		raw_spin_unlock_irqrestore(&iommu->register_lock, flag);		seq_putc(m, '\n');	}out:	rcu_read_unlock(); 	return ret;}DEFINE_SHOW_ATTRIBUTE(iommu_regset); 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\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[2],			   tbl_wlk->pasid_tbl_entry->val[1],			   tbl_wlk->pasid_tbl_entry->val[0]);} 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, 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; 		if (!context_present(context))			continue; 		tbl_wlk.bus = bus;		tbl_wlk.devfn = devfn;		tbl_wlk.rt_entry = &iommu->root_entry[bus];		tbl_wlk.ctx_entry = context;		m->private = &tbl_wlk; 		if (readq(iommu->reg + DMAR_RTADDR_REG) & DMA_RTADDR_SMT) {			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);	}} static void root_tbl_walk(struct seq_file *m, struct intel_iommu *iommu){	u16 bus; 	spin_lock(&iommu->lock);	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\t\t\t\tPASID\tPASID_table_entry\n"); 	/*	 * No need to check if the root entry is present or not because	 * iommu_context_addr() performs the same check before returning	 * context entry.	 */	for (bus = 0; bus < 256; bus++)		ctx_tbl_walk(m, iommu, bus);	spin_unlock(&iommu->lock);} static int dmar_translation_struct_show(struct seq_file *m, void *unused){	struct dmar_drhd_unit *drhd;	struct intel_iommu *iommu;	u32 sts; 	rcu_read_lock();	for_each_active_iommu(iommu, drhd) {		sts = readl(iommu->reg + DMAR_GSTS_REG);		if (!(sts & DMA_GSTS_TES)) {			seq_printf(m, "DMA Remapping is not enabled on %s\n",				   iommu->name);			continue;		}		root_tbl_walk(m, iommu);		seq_putc(m, '\n');	}	rcu_read_unlock(); 	return 0;}DEFINE_SHOW_ATTRIBUTE(dmar_translation_struct); static inline unsigned long level_to_directory_size(int level){	return BIT_ULL(VTD_PAGE_SHIFT + VTD_STRIDE_SHIFT * (level - 1));} static inline voiddump_page_info(struct seq_file *m, unsigned long iova, u64 *path){	seq_printf(m, "0x%013lx |\t0x%016llx\t0x%016llx\t0x%016llx",		   iova >> VTD_PAGE_SHIFT, path[5], path[4], path[3]);	if (path[2]) {		seq_printf(m, "\t0x%016llx", path[2]);		if (path[1])			seq_printf(m, "\t0x%016llx", path[1]);	}	seq_putc(m, '\n');} static void pgtable_walk_level(struct seq_file *m, struct dma_pte *pde,			       int level, unsigned long start,			       u64 *path){	int i; 	if (level > 5 || level < 1)		return; 	for (i = 0; i < BIT_ULL(VTD_STRIDE_SHIFT);			i++, pde++, start += level_to_directory_size(level)) {		if (!dma_pte_present(pde))			continue; 		path[level] = pde->val;		if (dma_pte_superpage(pde) || level == 1)			dump_page_info(m, start, path);		else			pgtable_walk_level(m, phys_to_virt(dma_pte_addr(pde)),					   level - 1, start, path);		path[level] = 0;	}} static int domain_translation_struct_show(struct seq_file *m,					  struct device_domain_info *info,					  ioasid_t pasid){	bool scalable, found = false;	struct dmar_drhd_unit *drhd;	struct intel_iommu *iommu;	u16 devfn, bus, seg; 	bus = info->bus;	devfn = info->devfn;	seg = info->segment; 	rcu_read_lock();	for_each_active_iommu(iommu, drhd) {		struct context_entry *context;		u64 pgd, path[6] = { 0 };		u32 sts, agaw; 		if (seg != iommu->segment)			continue; 		sts = readl(iommu->reg + DMAR_GSTS_REG);		if (!(sts & DMA_GSTS_TES)) {			seq_printf(m, "DMA Remapping is not enabled on %s\n",				   iommu->name);			continue;		}		if (readq(iommu->reg + DMAR_RTADDR_REG) & DMA_RTADDR_SMT)			scalable = true;		else			scalable = false; 		/*		 * The iommu->lock is held across the callback, which will		 * block calls to domain_attach/domain_detach. Hence,		 * the domain of the device will not change during traversal.		 *		 * Traversing page table possibly races with the iommu_unmap()		 * interface. This could be solved by RCU-freeing the page		 * table pages in the iommu_unmap() path.		 */		spin_lock(&iommu->lock); 		context = iommu_context_addr(iommu, bus, devfn, 0);		if (!context || !context_present(context))			goto iommu_unlock; 		if (scalable) {	/* scalable mode */			struct pasid_entry *pasid_tbl, *pasid_tbl_entry;			struct pasid_dir_entry *dir_tbl, *dir_entry;			u16 dir_idx, tbl_idx, pgtt;			u64 pasid_dir_ptr; 			pasid_dir_ptr = context->lo & VTD_PAGE_MASK; 			/* Dump specified device domain mappings with PASID. */			dir_idx = pasid >> PASID_PDE_SHIFT;			tbl_idx = pasid & PASID_PTE_MASK; 			dir_tbl = phys_to_virt(pasid_dir_ptr);			dir_entry = &dir_tbl[dir_idx]; 			pasid_tbl = get_pasid_table_from_pde(dir_entry);			if (!pasid_tbl)				goto iommu_unlock; 			pasid_tbl_entry = &pasid_tbl[tbl_idx];			if (!pasid_pte_is_present(pasid_tbl_entry))				goto iommu_unlock; 			/*			 * According to PASID Granular Translation Type(PGTT),			 * get the page table pointer.			 */			pgtt = (u16)(pasid_tbl_entry->val[0] & GENMASK_ULL(8, 6)) >> 6;			agaw = (u8)(pasid_tbl_entry->val[0] & GENMASK_ULL(4, 2)) >> 2; 			switch (pgtt) {			case PASID_ENTRY_PGTT_FL_ONLY:				pgd = pasid_tbl_entry->val[2];				break;			case PASID_ENTRY_PGTT_SL_ONLY:			case PASID_ENTRY_PGTT_NESTED:				pgd = pasid_tbl_entry->val[0];				break;			default:				goto iommu_unlock;			}			pgd &= VTD_PAGE_MASK;		} else { /* legacy mode */			u8 tt = (u8)(context->lo & GENMASK_ULL(3, 2)) >> 2; 			/*			 * According to Translation Type(TT),			 * get the page table pointer(SSPTPTR).			 */			switch (tt) {			case CONTEXT_TT_MULTI_LEVEL:			case CONTEXT_TT_DEV_IOTLB:				pgd = context->lo & VTD_PAGE_MASK;				agaw = context->hi & 7;				break;			default:				goto iommu_unlock;			}		} 		seq_printf(m, "Device %04x:%02x:%02x.%x ",			   iommu->segment, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); 		if (scalable)			seq_printf(m, "with pasid %x @0x%llx\n", pasid, pgd);		else			seq_printf(m, "@0x%llx\n", pgd); 		seq_printf(m, "%-17s\t%-18s\t%-18s\t%-18s\t%-18s\t%-s\n",			   "IOVA_PFN", "PML5E", "PML4E", "PDPE", "PDE", "PTE");		pgtable_walk_level(m, phys_to_virt(pgd), agaw + 2, 0, path); 		found = true;iommu_unlock:		spin_unlock(&iommu->lock);		if (found)			break;	}	rcu_read_unlock(); 	return 0;} static int dev_domain_translation_struct_show(struct seq_file *m, void *unused){	struct device_domain_info *info = (struct device_domain_info *)m->private; 	return domain_translation_struct_show(m, info, IOMMU_NO_PASID);}DEFINE_SHOW_ATTRIBUTE(dev_domain_translation_struct); static int pasid_domain_translation_struct_show(struct seq_file *m, void *unused){	struct dev_pasid_info *dev_pasid = (struct dev_pasid_info *)m->private;	struct device_domain_info *info = dev_iommu_priv_get(dev_pasid->dev); 	return domain_translation_struct_show(m, info, dev_pasid->pasid);}DEFINE_SHOW_ATTRIBUTE(pasid_domain_translation_struct); static void invalidation_queue_entry_show(struct seq_file *m,					  struct intel_iommu *iommu){	int index, shift = qi_shift(iommu);	struct qi_desc *desc;	int offset; 	if (ecap_smts(iommu->ecap))

Lines 1–500 of 807.