forked from mirrors/linux
		
	iommu/vt-d: Setup context and enable RID2PASID support
This patch enables the translation for requests without PASID in the scalable mode by setting up the root and context entries. Cc: Ashok Raj <ashok.raj@intel.com> Cc: Jacob Pan <jacob.jun.pan@linux.intel.com> Cc: Kevin Tian <kevin.tian@intel.com> Signed-off-by: Sanjay Kumar <sanjay.k.kumar@intel.com> Signed-off-by: Liu Yi L <yi.l.liu@intel.com> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Ashok Raj <ashok.raj@intel.com> Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
		
							parent
							
								
									ca6e322d60
								
							
						
					
					
						commit
						7373a8cc38
					
				
					 3 changed files with 108 additions and 30 deletions
				
			
		| 
						 | 
				
			
			@ -1197,6 +1197,8 @@ static void iommu_set_root_entry(struct intel_iommu *iommu)
 | 
			
		|||
	unsigned long flag;
 | 
			
		||||
 | 
			
		||||
	addr = virt_to_phys(iommu->root_entry);
 | 
			
		||||
	if (sm_supported(iommu))
 | 
			
		||||
		addr |= DMA_RTADDR_SMT;
 | 
			
		||||
 | 
			
		||||
	raw_spin_lock_irqsave(&iommu->register_lock, flag);
 | 
			
		||||
	dmar_writeq(iommu->reg + DMAR_RTADDR_REG, addr);
 | 
			
		||||
| 
						 | 
				
			
			@ -1918,6 +1920,56 @@ static void domain_exit(struct dmar_domain *domain)
 | 
			
		|||
	free_domain_mem(domain);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Get the PASID directory size for scalable mode context entry.
 | 
			
		||||
 * Value of X in the PDTS field of a scalable mode context entry
 | 
			
		||||
 * indicates PASID directory with 2^(X + 7) entries.
 | 
			
		||||
 */
 | 
			
		||||
static inline unsigned long context_get_sm_pds(struct pasid_table *table)
 | 
			
		||||
{
 | 
			
		||||
	int pds, max_pde;
 | 
			
		||||
 | 
			
		||||
	max_pde = table->max_pasid >> PASID_PDE_SHIFT;
 | 
			
		||||
	pds = find_first_bit((unsigned long *)&max_pde, MAX_NR_PASID_BITS);
 | 
			
		||||
	if (pds < 7)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	return pds - 7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set the RID_PASID field of a scalable mode context entry. The
 | 
			
		||||
 * IOMMU hardware will use the PASID value set in this field for
 | 
			
		||||
 * DMA translations of DMA requests without PASID.
 | 
			
		||||
 */
 | 
			
		||||
static inline void
 | 
			
		||||
context_set_sm_rid2pasid(struct context_entry *context, unsigned long pasid)
 | 
			
		||||
{
 | 
			
		||||
	context->hi |= pasid & ((1 << 20) - 1);
 | 
			
		||||
	context->hi |= (1 << 20);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set the DTE(Device-TLB Enable) field of a scalable mode context
 | 
			
		||||
 * entry.
 | 
			
		||||
 */
 | 
			
		||||
static inline void context_set_sm_dte(struct context_entry *context)
 | 
			
		||||
{
 | 
			
		||||
	context->lo |= (1 << 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set the PRE(Page Request Enable) field of a scalable mode context
 | 
			
		||||
 * entry.
 | 
			
		||||
 */
 | 
			
		||||
static inline void context_set_sm_pre(struct context_entry *context)
 | 
			
		||||
{
 | 
			
		||||
	context->lo |= (1 << 4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convert value to context PASID directory size field coding. */
 | 
			
		||||
#define context_pdts(pds)	(((pds) & 0x7) << 9)
 | 
			
		||||
 | 
			
		||||
static int domain_context_mapping_one(struct dmar_domain *domain,
 | 
			
		||||
				      struct intel_iommu *iommu,
 | 
			
		||||
				      struct pasid_table *table,
 | 
			
		||||
| 
						 | 
				
			
			@ -1928,8 +1980,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
 | 
			
		|||
	struct device_domain_info *info = NULL;
 | 
			
		||||
	struct context_entry *context;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	struct dma_pte *pgd;
 | 
			
		||||
	int ret, agaw;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	WARN_ON(did == 0);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1975,41 +2026,67 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pgd = domain->pgd;
 | 
			
		||||
 | 
			
		||||
	context_clear_entry(context);
 | 
			
		||||
	context_set_domain_id(context, did);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Skip top levels of page tables for iommu which has less agaw
 | 
			
		||||
	 * than default.  Unnecessary for PT mode.
 | 
			
		||||
	 */
 | 
			
		||||
	if (translation != CONTEXT_TT_PASS_THROUGH) {
 | 
			
		||||
		for (agaw = domain->agaw; agaw > iommu->agaw; agaw--) {
 | 
			
		||||
			ret = -ENOMEM;
 | 
			
		||||
			pgd = phys_to_virt(dma_pte_addr(pgd));
 | 
			
		||||
			if (!dma_pte_present(pgd))
 | 
			
		||||
				goto out_unlock;
 | 
			
		||||
		}
 | 
			
		||||
	if (sm_supported(iommu)) {
 | 
			
		||||
		unsigned long pds;
 | 
			
		||||
 | 
			
		||||
		WARN_ON(!table);
 | 
			
		||||
 | 
			
		||||
		/* Setup the PASID DIR pointer: */
 | 
			
		||||
		pds = context_get_sm_pds(table);
 | 
			
		||||
		context->lo = (u64)virt_to_phys(table->table) |
 | 
			
		||||
				context_pdts(pds);
 | 
			
		||||
 | 
			
		||||
		/* Setup the RID_PASID field: */
 | 
			
		||||
		context_set_sm_rid2pasid(context, PASID_RID2PASID);
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Setup the Device-TLB enable bit and Page request
 | 
			
		||||
		 * Enable bit:
 | 
			
		||||
		 */
 | 
			
		||||
		info = iommu_support_dev_iotlb(domain, iommu, bus, devfn);
 | 
			
		||||
		if (info && info->ats_supported)
 | 
			
		||||
			translation = CONTEXT_TT_DEV_IOTLB;
 | 
			
		||||
		else
 | 
			
		||||
			translation = CONTEXT_TT_MULTI_LEVEL;
 | 
			
		||||
 | 
			
		||||
		context_set_address_root(context, virt_to_phys(pgd));
 | 
			
		||||
		context_set_address_width(context, agaw);
 | 
			
		||||
			context_set_sm_dte(context);
 | 
			
		||||
		if (info && info->pri_supported)
 | 
			
		||||
			context_set_sm_pre(context);
 | 
			
		||||
	} else {
 | 
			
		||||
		/*
 | 
			
		||||
		 * In pass through mode, AW must be programmed to
 | 
			
		||||
		 * indicate the largest AGAW value supported by
 | 
			
		||||
		 * hardware. And ASR is ignored by hardware.
 | 
			
		||||
		 */
 | 
			
		||||
		context_set_address_width(context, iommu->msagaw);
 | 
			
		||||
		struct dma_pte *pgd = domain->pgd;
 | 
			
		||||
		int agaw;
 | 
			
		||||
 | 
			
		||||
		context_set_domain_id(context, did);
 | 
			
		||||
		context_set_translation_type(context, translation);
 | 
			
		||||
 | 
			
		||||
		if (translation != CONTEXT_TT_PASS_THROUGH) {
 | 
			
		||||
			/*
 | 
			
		||||
			 * Skip top levels of page tables for iommu which has
 | 
			
		||||
			 * less agaw than default. Unnecessary for PT mode.
 | 
			
		||||
			 */
 | 
			
		||||
			for (agaw = domain->agaw; agaw > iommu->agaw; agaw--) {
 | 
			
		||||
				ret = -ENOMEM;
 | 
			
		||||
				pgd = phys_to_virt(dma_pte_addr(pgd));
 | 
			
		||||
				if (!dma_pte_present(pgd))
 | 
			
		||||
					goto out_unlock;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			info = iommu_support_dev_iotlb(domain, iommu, bus, devfn);
 | 
			
		||||
			if (info && info->ats_supported)
 | 
			
		||||
				translation = CONTEXT_TT_DEV_IOTLB;
 | 
			
		||||
			else
 | 
			
		||||
				translation = CONTEXT_TT_MULTI_LEVEL;
 | 
			
		||||
 | 
			
		||||
			context_set_address_root(context, virt_to_phys(pgd));
 | 
			
		||||
			context_set_address_width(context, agaw);
 | 
			
		||||
		} else {
 | 
			
		||||
			/*
 | 
			
		||||
			 * In pass through mode, AW must be programmed to
 | 
			
		||||
			 * indicate the largest AGAW value supported by
 | 
			
		||||
			 * hardware. And ASR is ignored by hardware.
 | 
			
		||||
			 */
 | 
			
		||||
			context_set_address_width(context, iommu->msagaw);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	context_set_translation_type(context, translation);
 | 
			
		||||
	context_set_fault_enable(context);
 | 
			
		||||
	context_set_present(context);
 | 
			
		||||
	domain_flush_cache(domain, context, sizeof(*context));
 | 
			
		||||
| 
						 | 
				
			
			@ -5180,7 +5257,6 @@ static void intel_iommu_put_resv_regions(struct device *dev,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_INTEL_IOMMU_SVM
 | 
			
		||||
#define MAX_NR_PASID_BITS (20)
 | 
			
		||||
static inline unsigned long intel_iommu_get_pts(struct device *dev)
 | 
			
		||||
{
 | 
			
		||||
	int pts, max_pasid;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@
 | 
			
		|||
#define PASID_PTE_PRESENT		1
 | 
			
		||||
#define PDE_PFN_MASK			PAGE_MASK
 | 
			
		||||
#define PASID_PDE_SHIFT			6
 | 
			
		||||
#define MAX_NR_PASID_BITS		20
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Domain ID reserved for pasid entries programmed for first-level
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -258,6 +258,7 @@
 | 
			
		|||
 | 
			
		||||
/* DMA_RTADDR_REG */
 | 
			
		||||
#define DMA_RTADDR_RTT (((u64)1) << 11)
 | 
			
		||||
#define DMA_RTADDR_SMT (((u64)1) << 10)
 | 
			
		||||
 | 
			
		||||
/* CCMD_REG */
 | 
			
		||||
#define DMA_CCMD_ICC (((u64)1) << 63)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue