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;
 | 
						unsigned long flag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	addr = virt_to_phys(iommu->root_entry);
 | 
						addr = virt_to_phys(iommu->root_entry);
 | 
				
			||||||
 | 
						if (sm_supported(iommu))
 | 
				
			||||||
 | 
							addr |= DMA_RTADDR_SMT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	raw_spin_lock_irqsave(&iommu->register_lock, flag);
 | 
						raw_spin_lock_irqsave(&iommu->register_lock, flag);
 | 
				
			||||||
	dmar_writeq(iommu->reg + DMAR_RTADDR_REG, addr);
 | 
						dmar_writeq(iommu->reg + DMAR_RTADDR_REG, addr);
 | 
				
			||||||
| 
						 | 
					@ -1918,6 +1920,56 @@ static void domain_exit(struct dmar_domain *domain)
 | 
				
			||||||
	free_domain_mem(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,
 | 
					static int domain_context_mapping_one(struct dmar_domain *domain,
 | 
				
			||||||
				      struct intel_iommu *iommu,
 | 
									      struct intel_iommu *iommu,
 | 
				
			||||||
				      struct pasid_table *table,
 | 
									      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 device_domain_info *info = NULL;
 | 
				
			||||||
	struct context_entry *context;
 | 
						struct context_entry *context;
 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
	struct dma_pte *pgd;
 | 
						int ret;
 | 
				
			||||||
	int ret, agaw;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WARN_ON(did == 0);
 | 
						WARN_ON(did == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1975,16 +2026,42 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pgd = domain->pgd;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	context_clear_entry(context);
 | 
						context_clear_entry(context);
 | 
				
			||||||
	context_set_domain_id(context, did);
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
	 * Skip top levels of page tables for iommu which has less agaw
 | 
							 * Setup the Device-TLB enable bit and Page request
 | 
				
			||||||
	 * than default.  Unnecessary for PT mode.
 | 
							 * Enable bit:
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
 | 
							info = iommu_support_dev_iotlb(domain, iommu, bus, devfn);
 | 
				
			||||||
 | 
							if (info && info->ats_supported)
 | 
				
			||||||
 | 
								context_set_sm_dte(context);
 | 
				
			||||||
 | 
							if (info && info->pri_supported)
 | 
				
			||||||
 | 
								context_set_sm_pre(context);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							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) {
 | 
							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--) {
 | 
								for (agaw = domain->agaw; agaw > iommu->agaw; agaw--) {
 | 
				
			||||||
				ret = -ENOMEM;
 | 
									ret = -ENOMEM;
 | 
				
			||||||
				pgd = phys_to_virt(dma_pte_addr(pgd));
 | 
									pgd = phys_to_virt(dma_pte_addr(pgd));
 | 
				
			||||||
| 
						 | 
					@ -2008,8 +2085,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			context_set_address_width(context, iommu->msagaw);
 | 
								context_set_address_width(context, iommu->msagaw);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	context_set_translation_type(context, translation);
 | 
					 | 
				
			||||||
	context_set_fault_enable(context);
 | 
						context_set_fault_enable(context);
 | 
				
			||||||
	context_set_present(context);
 | 
						context_set_present(context);
 | 
				
			||||||
	domain_flush_cache(domain, context, sizeof(*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
 | 
					#ifdef CONFIG_INTEL_IOMMU_SVM
 | 
				
			||||||
#define MAX_NR_PASID_BITS (20)
 | 
					 | 
				
			||||||
static inline unsigned long intel_iommu_get_pts(struct device *dev)
 | 
					static inline unsigned long intel_iommu_get_pts(struct device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int pts, max_pasid;
 | 
						int pts, max_pasid;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@
 | 
				
			||||||
#define PASID_PTE_PRESENT		1
 | 
					#define PASID_PTE_PRESENT		1
 | 
				
			||||||
#define PDE_PFN_MASK			PAGE_MASK
 | 
					#define PDE_PFN_MASK			PAGE_MASK
 | 
				
			||||||
#define PASID_PDE_SHIFT			6
 | 
					#define PASID_PDE_SHIFT			6
 | 
				
			||||||
 | 
					#define MAX_NR_PASID_BITS		20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Domain ID reserved for pasid entries programmed for first-level
 | 
					 * Domain ID reserved for pasid entries programmed for first-level
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -258,6 +258,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* DMA_RTADDR_REG */
 | 
					/* DMA_RTADDR_REG */
 | 
				
			||||||
#define DMA_RTADDR_RTT (((u64)1) << 11)
 | 
					#define DMA_RTADDR_RTT (((u64)1) << 11)
 | 
				
			||||||
 | 
					#define DMA_RTADDR_SMT (((u64)1) << 10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* CCMD_REG */
 | 
					/* CCMD_REG */
 | 
				
			||||||
#define DMA_CCMD_ICC (((u64)1) << 63)
 | 
					#define DMA_CCMD_ICC (((u64)1) << 63)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue