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,41 +2026,67 @@ 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)) {
|
||||||
* Skip top levels of page tables for iommu which has less agaw
|
unsigned long pds;
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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);
|
info = iommu_support_dev_iotlb(domain, iommu, bus, devfn);
|
||||||
if (info && info->ats_supported)
|
if (info && info->ats_supported)
|
||||||
translation = CONTEXT_TT_DEV_IOTLB;
|
context_set_sm_dte(context);
|
||||||
else
|
if (info && info->pri_supported)
|
||||||
translation = CONTEXT_TT_MULTI_LEVEL;
|
context_set_sm_pre(context);
|
||||||
|
|
||||||
context_set_address_root(context, virt_to_phys(pgd));
|
|
||||||
context_set_address_width(context, agaw);
|
|
||||||
} else {
|
} else {
|
||||||
/*
|
struct dma_pte *pgd = domain->pgd;
|
||||||
* In pass through mode, AW must be programmed to
|
int agaw;
|
||||||
* indicate the largest AGAW value supported by
|
|
||||||
* hardware. And ASR is ignored by hardware.
|
context_set_domain_id(context, did);
|
||||||
*/
|
context_set_translation_type(context, translation);
|
||||||
context_set_address_width(context, iommu->msagaw);
|
|
||||||
|
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_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…
Add table
Reference in a new issue