Loading drivers/iommu/arm-smmu-qcom.c +9 −4 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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); Loading drivers/iommu/arm-smmu.c +212 −41 Original line number Diff line number Diff line Loading @@ -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}, }; Loading Loading @@ -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) Loading Loading @@ -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; Loading @@ -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) Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) Loading Loading @@ -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; Loading Loading @@ -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, Loading @@ -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->pgtbl_ops[0] = alloc_io_pgtable_ops(fmt, &ttbr0_pgtbl_info->pgtbl_cfg, smmu_domain); if (!smmu_domain->pgtbl_ops) { 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 Loading @@ -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) Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -2849,19 +2930,66 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) 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); Loading Loading @@ -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; } Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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); Loading drivers/iommu/arm-smmu.h +14 −2 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading drivers/iommu/iommu-debug.c +2 −0 Original line number Diff line number Diff line Loading @@ -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!"; } Loading include/linux/iommu.h +2 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
drivers/iommu/arm-smmu-qcom.c +9 −4 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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); Loading
drivers/iommu/arm-smmu.c +212 −41 Original line number Diff line number Diff line Loading @@ -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}, }; Loading Loading @@ -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) Loading Loading @@ -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; Loading @@ -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) Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) Loading Loading @@ -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; Loading Loading @@ -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, Loading @@ -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->pgtbl_ops[0] = alloc_io_pgtable_ops(fmt, &ttbr0_pgtbl_info->pgtbl_cfg, smmu_domain); if (!smmu_domain->pgtbl_ops) { 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 Loading @@ -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) Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -2849,19 +2930,66 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) 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); Loading Loading @@ -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; } Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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); Loading
drivers/iommu/arm-smmu.h +14 −2 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading
drivers/iommu/iommu-debug.c +2 −0 Original line number Diff line number Diff line Loading @@ -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!"; } Loading
include/linux/iommu.h +2 −1 Original line number Diff line number Diff line Loading @@ -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