mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-08 21:22:09 -04:00
Merge "iommu/arm-smmu: Add support for split page table domains"
This commit is contained in:
commit
f23c019559
5 changed files with 239 additions and 48 deletions
|
|
@ -518,15 +518,20 @@ static phys_addr_t qsmmuv500_iova_to_phys(
|
|||
int ret;
|
||||
phys_addr_t phys = 0;
|
||||
u64 val, fsr;
|
||||
long iova_ext_bits = (s64)iova >> smmu->va_size;
|
||||
bool split_tables = test_bit(DOMAIN_ATTR_SPLIT_TABLES,
|
||||
smmu_domain->attributes);
|
||||
unsigned long flags;
|
||||
int idx = cfg->cbndx;
|
||||
u32 sctlr_orig, sctlr;
|
||||
int needs_redo = 0;
|
||||
ktime_t timeout;
|
||||
|
||||
/* only 36 bit iova is supported */
|
||||
if (iova >= (1ULL << 36)) {
|
||||
dev_err_ratelimited(smmu->dev, "ECATS: address too large: %pad\n",
|
||||
if (iova_ext_bits && split_tables)
|
||||
iova_ext_bits = ~iova_ext_bits;
|
||||
|
||||
if (iova_ext_bits) {
|
||||
dev_err_ratelimited(smmu->dev, "ECATS: address out of bounds: %pad\n",
|
||||
&iova);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -769,7 +774,7 @@ static void qsmmuv500_init_cb(struct arm_smmu_domain *smmu_domain,
|
|||
if (!iommudata->has_actlr)
|
||||
return;
|
||||
|
||||
tlb = smmu_domain->pgtbl_info.pgtbl_cfg.tlb;
|
||||
tlb = smmu_domain->pgtbl_info[0].pgtbl_cfg.tlb;
|
||||
|
||||
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_ACTLR, iommudata->actlr);
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ static struct arm_smmu_option_prop arm_smmu_options[] = {
|
|||
{ ARM_SMMU_OPT_3LVL_TABLES, "qcom,use-3-lvl-tables" },
|
||||
{ ARM_SMMU_OPT_NO_ASID_RETENTION, "qcom,no-asid-retention" },
|
||||
{ ARM_SMMU_OPT_DISABLE_ATOS, "qcom,disable-atos" },
|
||||
{ ARM_SMMU_OPT_SPLIT_TABLES, "qcom,split-tables" },
|
||||
{ 0, NULL},
|
||||
};
|
||||
|
||||
|
|
@ -1214,10 +1215,20 @@ static phys_addr_t arm_smmu_verify_fault(struct iommu_domain *domain,
|
|||
{
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
struct msm_io_pgtable_info *pgtbl_info = &smmu_domain->pgtbl_info;
|
||||
struct msm_io_pgtable_info *pgtbl_info = &smmu_domain->pgtbl_info[0];
|
||||
phys_addr_t phys_hard_priv = 0;
|
||||
phys_addr_t phys_stimu, phys_stimu_post_tlbiall;
|
||||
unsigned long flags = 0;
|
||||
unsigned int ias = smmu_domain->pgtbl_info[0].pgtbl_cfg.ias;
|
||||
|
||||
/*
|
||||
* The address in the CB's FAR is not sign-extended, so lets perform the
|
||||
* sign extension here, as arm_smmu_iova_to_phys_hard() expects the
|
||||
* IOVA to be sign extended.
|
||||
*/
|
||||
if ((iova & BIT_ULL(ias)) &&
|
||||
(test_bit(DOMAIN_ATTR_SPLIT_TABLES, smmu_domain->attributes)))
|
||||
iova |= GENMASK_ULL(63, ias + 1);
|
||||
|
||||
/* Get the transaction type */
|
||||
if (fsynr0 & FSYNR0_WNR)
|
||||
|
|
@ -1438,12 +1449,48 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static u32 arm_smmu_tcr(u64 tcr, bool split_tables)
|
||||
{
|
||||
u32 tcr_out, tcr0, tcr1, tg0;
|
||||
|
||||
if (split_tables) {
|
||||
tcr0 = FIELD_GET(TCR_TCR0, tcr);
|
||||
/*
|
||||
* The TCR configuration for TTBR1 is identical
|
||||
* to the TCR configuration for TTBR0, except
|
||||
* for the TG field, so translate the TCR0
|
||||
* settings to TCR1 settings by shifting them
|
||||
*/
|
||||
tcr1 = FIELD_PREP(TCR_TCR1, tcr0);
|
||||
tcr1 &= ~TCR1_TG1;
|
||||
|
||||
/* Map TG0 -> TG1 */
|
||||
tg0 = FIELD_GET(TCR0_TG0, tcr0);
|
||||
if (tg0 == TCR0_TG0_4K)
|
||||
tcr1 |= FIELD_PREP(TCR1_TG1, TCR1_TG1_4K);
|
||||
else if (tg0 == TCR0_TG0_64K)
|
||||
tcr1 |= FIELD_PREP(TCR1_TG1, TCR1_TG1_64K);
|
||||
else if (tg0 == TCR0_TG0_16K)
|
||||
tcr1 |= FIELD_PREP(TCR1_TG1, TCR1_TG1_16K);
|
||||
|
||||
tcr_out = tcr1 | tcr0;
|
||||
} else {
|
||||
tcr_out = lower_32_bits(tcr);
|
||||
}
|
||||
|
||||
return tcr_out;
|
||||
}
|
||||
|
||||
static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
|
||||
struct io_pgtable_cfg *pgtbl_cfg)
|
||||
struct msm_io_pgtable_info *pgtbl_info)
|
||||
{
|
||||
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
|
||||
struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx];
|
||||
struct io_pgtable_cfg *pgtbl_cfg = &pgtbl_info[0].pgtbl_cfg;
|
||||
struct io_pgtable_cfg *ttbr1_pgtbl_cfg = &pgtbl_info[1].pgtbl_cfg;
|
||||
bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
|
||||
bool split_tables = test_bit(DOMAIN_ATTR_SPLIT_TABLES,
|
||||
smmu_domain->attributes);
|
||||
|
||||
cb->cfg = cfg;
|
||||
|
||||
|
|
@ -1452,7 +1499,9 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
|
|||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
|
||||
cb->tcr[0] = pgtbl_cfg->arm_v7s_cfg.tcr;
|
||||
} else {
|
||||
cb->tcr[0] = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
|
||||
cb->tcr[0] =
|
||||
arm_smmu_tcr(pgtbl_cfg->arm_lpae_s1_cfg.tcr,
|
||||
split_tables);
|
||||
cb->tcr[1] = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32;
|
||||
cb->tcr[1] |= FIELD_PREP(TCR2_SEP, TCR2_SEP_UPSTREAM);
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
|
||||
|
|
@ -1470,8 +1519,15 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
|
|||
} else {
|
||||
cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
|
||||
cb->ttbr[0] |= FIELD_PREP(TTBRn_ASID, cfg->asid);
|
||||
cb->ttbr[1] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
|
||||
cb->ttbr[1] |= FIELD_PREP(TTBRn_ASID, cfg->asid);
|
||||
if (split_tables) {
|
||||
cb->ttbr[1] =
|
||||
ttbr1_pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
|
||||
} else {
|
||||
cb->ttbr[1] =
|
||||
pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
|
||||
cb->ttbr[1] |=
|
||||
FIELD_PREP(TTBRn_ASID, cfg->asid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cb->ttbr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
|
||||
|
|
@ -1727,7 +1783,7 @@ static int arm_smmu_get_dma_cookie(struct device *dev,
|
|||
bool s1_bypass = test_bit(DOMAIN_ATTR_S1_BYPASS,
|
||||
smmu_domain->attributes);
|
||||
struct iommu_domain *domain = &smmu_domain->domain.iommu_domain;
|
||||
struct io_pgtable_ops *pgtbl_ops = smmu_domain->pgtbl_ops;
|
||||
struct io_pgtable_ops *pgtbl_ops = smmu_domain->pgtbl_ops[0];
|
||||
|
||||
if (s1_bypass)
|
||||
return 0;
|
||||
|
|
@ -1768,13 +1824,13 @@ static int arm_smmu_setup_context_bank(struct arm_smmu_domain *smmu_domain,
|
|||
{
|
||||
struct iommu_domain *domain = &smmu_domain->domain.iommu_domain;
|
||||
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
|
||||
struct io_pgtable_cfg *pgtbl_cfg = &smmu_domain->pgtbl_info.pgtbl_cfg;
|
||||
bool dynamic = is_dynamic_domain(domain);
|
||||
int irq, ret = 0;
|
||||
|
||||
if (!dynamic) {
|
||||
/* Initialise the context bank with our page table cfg */
|
||||
arm_smmu_init_context_bank(smmu_domain, pgtbl_cfg);
|
||||
arm_smmu_init_context_bank(smmu_domain,
|
||||
smmu_domain->pgtbl_info);
|
||||
arm_smmu_write_context_bank(smmu, cfg->cbndx,
|
||||
smmu_domain->attributes);
|
||||
|
||||
|
|
@ -1816,11 +1872,15 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
unsigned long ias, oas;
|
||||
enum io_pgtable_fmt fmt;
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct msm_io_pgtable_info *pgtbl_info = &smmu_domain->pgtbl_info;
|
||||
struct msm_io_pgtable_info *ttbr0_pgtbl_info =
|
||||
&smmu_domain->pgtbl_info[0];
|
||||
struct msm_io_pgtable_info *ttbr1_pgtbl_info =
|
||||
&smmu_domain->pgtbl_info[1];
|
||||
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
|
||||
unsigned long quirks = 0;
|
||||
struct iommu_group *group;
|
||||
struct io_pgtable *iop;
|
||||
bool split_tables = false;
|
||||
|
||||
mutex_lock(&smmu_domain->init_mutex);
|
||||
if (smmu_domain->smmu)
|
||||
|
|
@ -1883,9 +1943,13 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
if ((IS_ENABLED(CONFIG_64BIT) || cfg->fmt == ARM_SMMU_CTX_FMT_NONE) &&
|
||||
(smmu->features & (ARM_SMMU_FEAT_FMT_AARCH64_64K |
|
||||
ARM_SMMU_FEAT_FMT_AARCH64_16K |
|
||||
ARM_SMMU_FEAT_FMT_AARCH64_4K)))
|
||||
ARM_SMMU_FEAT_FMT_AARCH64_4K))) {
|
||||
cfg->fmt = ARM_SMMU_CTX_FMT_AARCH64;
|
||||
|
||||
if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1 &&
|
||||
smmu->options & ARM_SMMU_OPT_SPLIT_TABLES)
|
||||
split_tables = test_bit(DOMAIN_ATTR_SPLIT_TABLES,
|
||||
smmu_domain->attributes);
|
||||
}
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_NONE) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
|
|
@ -1961,7 +2025,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
goto out_clear_smmu;
|
||||
}
|
||||
|
||||
pgtbl_info->pgtbl_cfg = (struct io_pgtable_cfg) {
|
||||
ttbr0_pgtbl_info->pgtbl_cfg = (struct io_pgtable_cfg) {
|
||||
.quirks = quirks,
|
||||
.pgsize_bitmap = smmu->pgsize_bitmap,
|
||||
.ias = ias,
|
||||
|
|
@ -1972,21 +2036,37 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
};
|
||||
|
||||
smmu_domain->dev = dev;
|
||||
smmu_domain->pgtbl_ops = alloc_io_pgtable_ops(fmt,
|
||||
&pgtbl_info->pgtbl_cfg,
|
||||
smmu_domain);
|
||||
if (!smmu_domain->pgtbl_ops) {
|
||||
smmu_domain->pgtbl_ops[0] = alloc_io_pgtable_ops(fmt,
|
||||
&ttbr0_pgtbl_info->pgtbl_cfg,
|
||||
smmu_domain);
|
||||
if (!smmu_domain->pgtbl_ops[0]) {
|
||||
ret = -ENOMEM;
|
||||
goto out_clear_smmu;
|
||||
}
|
||||
if (split_tables) {
|
||||
ttbr1_pgtbl_info->pgtbl_cfg = ttbr0_pgtbl_info->pgtbl_cfg;
|
||||
smmu_domain->pgtbl_ops[1] = alloc_io_pgtable_ops(fmt,
|
||||
&ttbr1_pgtbl_info->pgtbl_cfg,
|
||||
smmu_domain);
|
||||
if (!smmu_domain->pgtbl_ops[1]) {
|
||||
ret = -ENOMEM;
|
||||
goto out_clear_smmu;
|
||||
}
|
||||
}
|
||||
|
||||
group = iommu_group_get_for_dev(smmu_domain->dev);
|
||||
iop = container_of(smmu_domain->pgtbl_ops, struct io_pgtable, ops);
|
||||
iop = container_of(smmu_domain->pgtbl_ops[0], struct io_pgtable, ops);
|
||||
ret = iommu_logger_register(&smmu_domain->logger, domain, group, iop);
|
||||
iommu_group_put(group);
|
||||
if (ret)
|
||||
goto out_clear_smmu;
|
||||
|
||||
/*
|
||||
* Clear the attribute if we didn't actually set up the split tables
|
||||
* so that domain can query itself later
|
||||
*/
|
||||
if (!split_tables)
|
||||
clear_bit(DOMAIN_ATTR_SPLIT_TABLES, smmu_domain->attributes);
|
||||
/*
|
||||
* assign any page table memory that might have been allocated
|
||||
* during alloc_io_pgtable_ops
|
||||
|
|
@ -2000,7 +2080,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
arm_smmu_secure_domain_unlock(smmu_domain);
|
||||
|
||||
/* Update the domain's page sizes to reflect the page table format */
|
||||
domain->pgsize_bitmap = pgtbl_info->pgtbl_cfg.pgsize_bitmap;
|
||||
domain->pgsize_bitmap = ttbr0_pgtbl_info->pgtbl_cfg.pgsize_bitmap;
|
||||
domain->geometry.aperture_end = (1UL << ias) - 1;
|
||||
ret = arm_smmu_adjust_domain_geometry(dev, domain);
|
||||
if (ret)
|
||||
|
|
@ -2074,7 +2154,8 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
|
|||
dynamic = is_dynamic_domain(domain);
|
||||
if (dynamic) {
|
||||
arm_smmu_free_asid(domain);
|
||||
free_io_pgtable_ops(smmu_domain->pgtbl_ops);
|
||||
free_io_pgtable_ops(smmu_domain->pgtbl_ops[1]);
|
||||
free_io_pgtable_ops(smmu_domain->pgtbl_ops[0]);
|
||||
arm_smmu_power_off(smmu, smmu->pwr);
|
||||
arm_smmu_rpm_put(smmu);
|
||||
arm_smmu_secure_domain_lock(smmu_domain);
|
||||
|
|
@ -2097,13 +2178,13 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
|
|||
devm_free_irq(smmu->dev, irq, domain);
|
||||
}
|
||||
|
||||
free_io_pgtable_ops(smmu_domain->pgtbl_ops);
|
||||
free_io_pgtable_ops(smmu_domain->pgtbl_ops[1]);
|
||||
free_io_pgtable_ops(smmu_domain->pgtbl_ops[0]);
|
||||
arm_smmu_secure_domain_lock(smmu_domain);
|
||||
arm_smmu_secure_pool_destroy(smmu_domain);
|
||||
arm_smmu_unassign_table(smmu_domain);
|
||||
arm_smmu_secure_domain_unlock(smmu_domain);
|
||||
__arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
|
||||
|
||||
arm_smmu_power_off(smmu, smmu->pwr);
|
||||
arm_smmu_rpm_put(smmu);
|
||||
arm_smmu_domain_reinit(smmu_domain);
|
||||
|
|
@ -2391,7 +2472,7 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
|
|||
int i, idx;
|
||||
const struct iommu_flush_ops *tlb;
|
||||
|
||||
tlb = smmu_domain->pgtbl_info.pgtbl_cfg.tlb;
|
||||
tlb = smmu_domain->pgtbl_info[0].pgtbl_cfg.tlb;
|
||||
|
||||
mutex_lock(&smmu->stream_map_mutex);
|
||||
for_each_cfg_sme(fwspec, i, idx) {
|
||||
|
|
@ -2849,19 +2930,66 @@ out_power_off:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct io_pgtable_ops *arm_smmu_get_pgtable_ops(
|
||||
struct arm_smmu_domain *smmu_domain,
|
||||
unsigned long iova)
|
||||
{
|
||||
struct io_pgtable_cfg *cfg = &smmu_domain->pgtbl_info[0].pgtbl_cfg;
|
||||
long iova_ext_bits = (s64)iova >> cfg->ias;
|
||||
unsigned int idx = 0;
|
||||
bool split_tables_domain = test_bit(DOMAIN_ATTR_SPLIT_TABLES,
|
||||
smmu_domain->attributes);
|
||||
|
||||
if (!split_tables_domain)
|
||||
return smmu_domain->pgtbl_ops[0];
|
||||
|
||||
if (iova_ext_bits) {
|
||||
iova_ext_bits = ~iova_ext_bits;
|
||||
idx = 1;
|
||||
}
|
||||
|
||||
if (WARN_ON(iova_ext_bits))
|
||||
return ERR_PTR(-ERANGE);
|
||||
|
||||
return smmu_domain->pgtbl_ops[idx];
|
||||
}
|
||||
|
||||
/*
|
||||
* The ARM IO-Page-table code assumes that all mappings are for TTBR0. For
|
||||
* devices that use the upper portion of the IOVA space, this is a problem
|
||||
* as the upper portion of the address space is beyond the TTBR0 space, so the
|
||||
* IOVA code will forbid the mapping. To circumvent this, unconditionally mask
|
||||
* the sign extended bits. This should be okay, as those are the only bits
|
||||
* that are relevant anyway for indexing into the page tables.
|
||||
*/
|
||||
static unsigned long arm_smmu_mask_iova(struct arm_smmu_domain *smmu_domain,
|
||||
unsigned long iova)
|
||||
{
|
||||
unsigned int ias = smmu_domain->pgtbl_info[0].pgtbl_cfg.ias;
|
||||
unsigned long mask = (1UL << ias) - 1;
|
||||
|
||||
if (!test_bit(DOMAIN_ATTR_SPLIT_TABLES, smmu_domain->attributes))
|
||||
return iova;
|
||||
|
||||
return iova & mask;
|
||||
}
|
||||
|
||||
static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
|
||||
struct io_pgtable_ops *ops;
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
LIST_HEAD(nonsecure_pool);
|
||||
|
||||
if (!ops)
|
||||
return -ENODEV;
|
||||
|
||||
ops = arm_smmu_get_pgtable_ops(smmu_domain, iova);
|
||||
if (IS_ERR(ops))
|
||||
return PTR_ERR(ops);
|
||||
else if (!ops)
|
||||
return -EINVAL;
|
||||
iova = arm_smmu_mask_iova(smmu_domain, iova);
|
||||
arm_smmu_secure_domain_lock(smmu_domain);
|
||||
arm_smmu_rpm_get(smmu);
|
||||
spin_lock_irqsave(&smmu_domain->cb_lock, flags);
|
||||
|
|
@ -2898,13 +3026,18 @@ static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain,
|
|||
uint64_t ret;
|
||||
unsigned long flags;
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct msm_io_pgtable_info *pgtbl_info = &smmu_domain->pgtbl_info;
|
||||
struct msm_io_pgtable_info *pgtbl_info = &smmu_domain->pgtbl_info[0];
|
||||
struct io_pgtable_ops *ops;
|
||||
|
||||
if (!pgtbl_info->iova_to_pte)
|
||||
return 0;
|
||||
|
||||
ops = arm_smmu_get_pgtable_ops(smmu_domain, iova);
|
||||
if (IS_ERR_OR_NULL(ops))
|
||||
return 0;
|
||||
iova = arm_smmu_mask_iova(smmu_domain, iova);
|
||||
spin_lock_irqsave(&smmu_domain->cb_lock, flags);
|
||||
ret = pgtbl_info->iova_to_pte(smmu_domain->pgtbl_ops, iova);
|
||||
ret = pgtbl_info->iova_to_pte(ops, iova);
|
||||
spin_unlock_irqrestore(&smmu_domain->cb_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -2915,12 +3048,13 @@ static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
|
|||
size_t ret;
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
|
||||
struct io_pgtable_ops *ops;
|
||||
unsigned long flags;
|
||||
|
||||
if (!ops)
|
||||
ops = arm_smmu_get_pgtable_ops(smmu_domain, iova);
|
||||
if (IS_ERR_OR_NULL(ops))
|
||||
return 0;
|
||||
|
||||
iova = arm_smmu_mask_iova(smmu_domain, iova);
|
||||
ret = arm_smmu_domain_power_on(domain, smmu_domain->smmu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -2979,16 +3113,20 @@ static size_t arm_smmu_map_sg(struct iommu_domain *domain, unsigned long iova,
|
|||
size_t size, batch_size, size_to_unmap = 0;
|
||||
unsigned long flags;
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
|
||||
struct msm_io_pgtable_info *pgtbl_info = &smmu_domain->pgtbl_info;
|
||||
struct io_pgtable_ops *ops;
|
||||
struct msm_io_pgtable_info *pgtbl_info = &smmu_domain->pgtbl_info[0];
|
||||
unsigned int idx_start, idx_end;
|
||||
struct scatterlist *sg_start, *sg_end;
|
||||
unsigned long __saved_iova_start;
|
||||
LIST_HEAD(nonsecure_pool);
|
||||
|
||||
if (!pgtbl_info->map_sg)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
|
||||
ops = arm_smmu_get_pgtable_ops(smmu_domain, iova);
|
||||
if (IS_ERR_OR_NULL(ops))
|
||||
return 0;
|
||||
iova = arm_smmu_mask_iova(smmu_domain, iova);
|
||||
|
||||
arm_smmu_secure_domain_lock(smmu_domain);
|
||||
|
||||
|
|
@ -3063,7 +3201,7 @@ static phys_addr_t __arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
|
|||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
|
||||
struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
|
||||
struct io_pgtable_ops *ops;
|
||||
struct device *dev = smmu->dev;
|
||||
void __iomem *reg;
|
||||
u32 tmp;
|
||||
|
|
@ -3071,6 +3209,10 @@ static phys_addr_t __arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
|
|||
unsigned long va;
|
||||
int idx = cfg->cbndx;
|
||||
|
||||
ops = arm_smmu_get_pgtable_ops(smmu_domain, iova);
|
||||
if (IS_ERR_OR_NULL(ops))
|
||||
return 0;
|
||||
|
||||
va = iova & ~0xfffUL;
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
|
||||
arm_smmu_cb_writeq(smmu, idx, ARM_SMMU_CB_ATS1PR, va);
|
||||
|
|
@ -3105,12 +3247,14 @@ static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain,
|
|||
phys_addr_t ret;
|
||||
unsigned long flags;
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
|
||||
struct io_pgtable_ops *ops;
|
||||
|
||||
ops = arm_smmu_get_pgtable_ops(smmu_domain, iova);
|
||||
iova = arm_smmu_mask_iova(smmu_domain, iova);
|
||||
if (domain->type == IOMMU_DOMAIN_IDENTITY)
|
||||
return iova;
|
||||
|
||||
if (!ops)
|
||||
if (IS_ERR_OR_NULL(ops))
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&smmu_domain->cb_lock, flags);
|
||||
|
|
@ -3350,7 +3494,8 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
|
|||
enum iommu_attr attr, void *data)
|
||||
{
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct io_pgtable_cfg *pgtbl_cfg = &smmu_domain->pgtbl_info.pgtbl_cfg;
|
||||
struct io_pgtable_cfg *pgtbl_cfg =
|
||||
&smmu_domain->pgtbl_info[0].pgtbl_cfg;
|
||||
int ret = 0;
|
||||
unsigned long iommu_attr = (unsigned long)attr;
|
||||
|
||||
|
|
@ -3432,7 +3577,7 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
|
|||
ret = -ENODEV;
|
||||
break;
|
||||
}
|
||||
info->ops = smmu_domain->pgtbl_ops;
|
||||
info->ops = smmu_domain->pgtbl_ops[0];
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
|
@ -3485,6 +3630,11 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
|
|||
smmu_domain->attributes);
|
||||
ret = 0;
|
||||
break;
|
||||
case DOMAIN_ATTR_SPLIT_TABLES:
|
||||
*((int *)data) = test_bit(DOMAIN_ATTR_SPLIT_TABLES,
|
||||
smmu_domain->attributes);
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
break;
|
||||
|
|
@ -3733,6 +3883,22 @@ static int __arm_smmu_domain_set_attr2(struct iommu_domain *domain,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DOMAIN_ATTR_SPLIT_TABLES: {
|
||||
int split_tables = *((int *)data);
|
||||
/* can't be changed while attached */
|
||||
if (smmu_domain->smmu != NULL) {
|
||||
ret = -EBUSY;
|
||||
} else if (split_tables) {
|
||||
set_bit(DOMAIN_ATTR_SPLIT_TABLES,
|
||||
smmu_domain->attributes);
|
||||
ret = 0;
|
||||
} else {
|
||||
clear_bit(DOMAIN_ATTR_SPLIT_TABLES,
|
||||
smmu_domain->attributes);
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
|
@ -3817,12 +3983,17 @@ static bool arm_smmu_is_iova_coherent(struct iommu_domain *domain,
|
|||
bool ret;
|
||||
unsigned long flags;
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
|
||||
struct msm_io_pgtable_info *pgtbl_info = &smmu_domain->pgtbl_info;
|
||||
struct io_pgtable_ops *ops;
|
||||
struct msm_io_pgtable_info *pgtbl_info = &smmu_domain->pgtbl_info[0];
|
||||
|
||||
if (!pgtbl_info->is_iova_coherent)
|
||||
return false;
|
||||
|
||||
ops = arm_smmu_get_pgtable_ops(smmu_domain, iova);
|
||||
if (IS_ERR_OR_NULL(ops))
|
||||
return false;
|
||||
|
||||
iova = arm_smmu_mask_iova(smmu_domain, iova);
|
||||
spin_lock_irqsave(&smmu_domain->cb_lock, flags);
|
||||
ret = pgtbl_info->is_iova_coherent(ops, iova);
|
||||
spin_unlock_irqrestore(&smmu_domain->cb_lock, flags);
|
||||
|
|
|
|||
|
|
@ -180,6 +180,17 @@ enum arm_smmu_cbar_type {
|
|||
#define TTBRn_ASID GENMASK_ULL(63, 48)
|
||||
|
||||
#define ARM_SMMU_CB_TCR 0x30
|
||||
#define TCR_TCR1 GENMASK(31, 16)
|
||||
#define TCR1_TG1 GENMASK(31, 30)
|
||||
#define TCR1_TG1_16K 0x1
|
||||
#define TCR1_TG1_4K 0x2
|
||||
#define TCR1_TG1_64K 0x3
|
||||
#define TCR1_EPD1 BIT(23)
|
||||
#define TCR_TCR0 GENMASK(15, 0)
|
||||
#define TCR0_TG0 GENMASK(15, 14)
|
||||
#define TCR0_TG0_4K 0x0
|
||||
#define TCR0_TG0_64K 0x1
|
||||
#define TCR0_TG0_16K 0x2
|
||||
#define ARM_SMMU_CB_CONTEXTIDR 0x34
|
||||
#define ARM_SMMU_CB_S1_MAIR0 0x38
|
||||
#define ARM_SMMU_CB_S1_MAIR1 0x3c
|
||||
|
|
@ -352,6 +363,7 @@ struct arm_smmu_device {
|
|||
#define ARM_SMMU_OPT_3LVL_TABLES (1 << 2)
|
||||
#define ARM_SMMU_OPT_NO_ASID_RETENTION (1 << 3)
|
||||
#define ARM_SMMU_OPT_DISABLE_ATOS (1 << 4)
|
||||
#define ARM_SMMU_OPT_SPLIT_TABLES (1 << 5)
|
||||
u32 options;
|
||||
enum arm_smmu_arch_version version;
|
||||
enum arm_smmu_implementation model;
|
||||
|
|
@ -467,7 +479,7 @@ struct arm_smmu_flush_ops {
|
|||
struct arm_smmu_domain {
|
||||
struct arm_smmu_device *smmu;
|
||||
struct device *dev;
|
||||
struct io_pgtable_ops *pgtbl_ops;
|
||||
struct io_pgtable_ops *pgtbl_ops[2];
|
||||
const struct arm_smmu_flush_ops *flush_ops;
|
||||
struct arm_smmu_cfg cfg;
|
||||
enum arm_smmu_domain_stage stage;
|
||||
|
|
@ -475,7 +487,7 @@ struct arm_smmu_domain {
|
|||
struct mutex init_mutex; /* Protects smmu pointer */
|
||||
spinlock_t cb_lock; /* Serialises ATS1* ops */
|
||||
spinlock_t sync_lock; /* Serialises TLB syncs */
|
||||
struct msm_io_pgtable_info pgtbl_info;
|
||||
struct msm_io_pgtable_info pgtbl_info[2];
|
||||
DECLARE_BITMAP(attributes, DOMAIN_ATTR_EXTENDED_MAX);
|
||||
u32 secure_vmid;
|
||||
struct list_head pte_info_list;
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ static const char *iommu_debug_attr_to_string(enum iommu_attr attr)
|
|||
return "DOMAIN_ATTR_EARLY_MAP";
|
||||
case DOMAIN_ATTR_CB_STALL_DISABLE:
|
||||
return "DOMAIN_ATTR_CB_STALL_DISABLE";
|
||||
case DOMAIN_ATTR_SPLIT_TABLES:
|
||||
return "DOMAIN_ATTR_SPLIT_TABLES";
|
||||
default:
|
||||
return "Unknown attr!";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,7 +189,8 @@ enum iommu_attr {
|
|||
#define DOMAIN_ATTR_CB_STALL_DISABLE (EXTENDED_ATTR_BASE + 17)
|
||||
#define DOMAIN_ATTR_USE_LLC_NWA (EXTENDED_ATTR_BASE + 18)
|
||||
#define DOMAIN_ATTR_NO_CFRE (EXTENDED_ATTR_BASE + 19)
|
||||
#define DOMAIN_ATTR_EXTENDED_MAX (EXTENDED_ATTR_BASE + 20)
|
||||
#define DOMAIN_ATTR_SPLIT_TABLES (EXTENDED_ATTR_BASE + 20)
|
||||
#define DOMAIN_ATTR_EXTENDED_MAX (EXTENDED_ATTR_BASE + 21)
|
||||
|
||||
/* These are the possible reserved region types */
|
||||
enum iommu_resv_type {
|
||||
|
|
|
|||
Loading…
Reference in a new issue