From 4670e9e03ebf968d43eea9e1d3a66658e76f6e4f Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Thu, 15 Aug 2019 19:37:37 +0100 Subject: [PATCH] iommu/arm-smmu: Add context init implementation hook Allocating and initialising a context for a domain is another point where certain implementations are known to want special behaviour. Currently the other half of the Cavium workaround comes into play here, so let's finish the job to get the whole thing right out of the way. Change-Id: I2fc1b383a5084d6a92e99002ed12b610e0cf7b04 Signed-off-by: Robin Murphy Signed-off-by: Will Deacon Git-commit: ba7e4a08bbf7441664b3d140671db8d08ea15f22 Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [isaacm@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Isaac J. Manjarres --- drivers/iommu/arm-smmu-impl.c | 42 ++++++++++++++++++++++--- drivers/iommu/arm-smmu.c | 58 +++++------------------------------ drivers/iommu/arm-smmu.h | 54 ++++++++++++++++++++++++++++++-- 3 files changed, 97 insertions(+), 57 deletions(-) diff --git a/drivers/iommu/arm-smmu-impl.c b/drivers/iommu/arm-smmu-impl.c index 4dc8b1c4befb..e22e9004f449 100644 --- a/drivers/iommu/arm-smmu-impl.c +++ b/drivers/iommu/arm-smmu-impl.c @@ -48,25 +48,60 @@ const struct arm_smmu_impl calxeda_impl = { }; +struct cavium_smmu { + struct arm_smmu_device smmu; + u32 id_base; +}; + static int cavium_cfg_probe(struct arm_smmu_device *smmu) { static atomic_t context_count = ATOMIC_INIT(0); + struct cavium_smmu *cs = container_of(smmu, struct cavium_smmu, smmu); /* * Cavium CN88xx erratum #27704. * Ensure ASID and VMID allocation is unique across all SMMUs in * the system. */ - smmu->cavium_id_base = atomic_fetch_add(smmu->num_context_banks, - &context_count); + cs->id_base = atomic_fetch_add(smmu->num_context_banks, &context_count); dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n"); return 0; } +int cavium_init_context(struct arm_smmu_domain *smmu_domain) +{ + struct cavium_smmu *cs = container_of(smmu_domain->smmu, + struct cavium_smmu, smmu); + + if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2) + smmu_domain->cfg.vmid += cs->id_base; + else + smmu_domain->cfg.asid += cs->id_base; + + return 0; +} + const struct arm_smmu_impl cavium_impl = { .cfg_probe = cavium_cfg_probe, + .init_context = cavium_init_context, }; +struct arm_smmu_device *cavium_smmu_impl_init(struct arm_smmu_device *smmu) +{ + struct cavium_smmu *cs; + + cs = devm_kzalloc(smmu->dev, sizeof(*cs), GFP_KERNEL); + if (!cs) + return ERR_PTR(-ENOMEM); + + cs->smmu = *smmu; + cs->smmu.impl = &cavium_impl; + + devm_kfree(smmu->dev, smmu); + + return &cs->smmu; +} + #define ARM_MMU500_ACTLR_CPRE (1 << 1) @@ -126,8 +161,7 @@ struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) smmu->impl = &arm_mmu500_impl; break; case CAVIUM_SMMUV2: - smmu->impl = &cavium_impl; - break; + return cavium_smmu_impl_init(smmu); default: break; } diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 7332e983cb27..608e0569e994 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -144,25 +143,6 @@ struct arm_smmu_master_cfg { #define for_each_cfg_sme(fw, i, idx) \ for (i = 0; idx = fwspec_smendx(fw, i), i < fw->num_ids; ++i) -enum arm_smmu_context_fmt { - ARM_SMMU_CTX_FMT_NONE, - ARM_SMMU_CTX_FMT_AARCH64, - ARM_SMMU_CTX_FMT_AARCH32_L, - ARM_SMMU_CTX_FMT_AARCH32_S, -}; - -struct arm_smmu_cfg { - u8 cbndx; - u8 irptndx; - union { - u16 asid; - u16 vmid; - }; - u32 procid; - enum arm_smmu_cbar_type cbar; - enum arm_smmu_context_fmt fmt; -}; -#define INVALID_IRPTNDX 0xff #define INVALID_CBNDX 0xff #define INVALID_ASID 0xffff /* @@ -175,42 +155,12 @@ struct arm_smmu_cfg { #define ARM_SMMU_CB_VMID(smmu, cfg) ((u16)(smmu)->cavium_id_base + \ (cfg)->cbndx + 1) -enum arm_smmu_domain_stage { - ARM_SMMU_DOMAIN_S1 = 0, - ARM_SMMU_DOMAIN_S2, - ARM_SMMU_DOMAIN_NESTED, - ARM_SMMU_DOMAIN_BYPASS, -}; - struct arm_smmu_pte_info { void *virt_addr; size_t size; struct list_head entry; }; -struct arm_smmu_domain { - struct arm_smmu_device *smmu; - struct device *dev; - struct io_pgtable_ops *pgtbl_ops; - const struct iommu_gather_ops *tlb_ops; - struct arm_smmu_cfg cfg; - enum arm_smmu_domain_stage stage; - bool non_strict; - 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; - u64 attributes; - u32 secure_vmid; - struct list_head pte_info_list; - struct list_head unassign_list; - struct mutex assign_lock; - struct list_head secure_pool_list; - /* nonsecure pool protected by pgtbl_lock */ - struct list_head nonsecure_pool; - struct msm_iommu_domain domain; -}; - static bool using_legacy_binding, using_generic_binding; struct arm_smmu_option_prop { @@ -1958,6 +1908,13 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, cfg->cbndx = ret; + smmu_domain->smmu = smmu; + if (smmu->impl && smmu->impl->init_context) { + ret = smmu->impl->init_context(smmu_domain); + if (ret) + goto out_clear_smmu; + } + pgtbl_info->pgtbl_cfg = (struct io_pgtable_cfg) { .quirks = quirks, .pgsize_bitmap = smmu->pgsize_bitmap, @@ -1968,7 +1925,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, .iommu_dev = smmu->dev, }; - smmu_domain->smmu = smmu; smmu_domain->dev = dev; smmu_domain->pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_info->pgtbl_cfg, diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h index 0c6d6e13e891..e3e179ad8f51 100644 --- a/drivers/iommu/arm-smmu.h +++ b/drivers/iommu/arm-smmu.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -341,8 +342,6 @@ struct arm_smmu_device { struct list_head list; - u32 cavium_id_base; /* Specific to Cavium */ - spinlock_t global_sync_lock; /* IOMMU core code handle */ @@ -364,6 +363,56 @@ struct arm_smmu_device { void *archdata; }; +enum arm_smmu_context_fmt { + ARM_SMMU_CTX_FMT_NONE, + ARM_SMMU_CTX_FMT_AARCH64, + ARM_SMMU_CTX_FMT_AARCH32_L, + ARM_SMMU_CTX_FMT_AARCH32_S, +}; + +struct arm_smmu_cfg { + u8 cbndx; + u8 irptndx; + union { + u16 asid; + u16 vmid; + }; + u32 procid; + enum arm_smmu_cbar_type cbar; + enum arm_smmu_context_fmt fmt; +}; +#define INVALID_IRPTNDX 0xff + +enum arm_smmu_domain_stage { + ARM_SMMU_DOMAIN_S1 = 0, + ARM_SMMU_DOMAIN_S2, + ARM_SMMU_DOMAIN_NESTED, + ARM_SMMU_DOMAIN_BYPASS, +}; + +struct arm_smmu_domain { + struct arm_smmu_device *smmu; + struct device *dev; + struct io_pgtable_ops *pgtbl_ops; + const struct iommu_gather_ops *tlb_ops; + struct arm_smmu_cfg cfg; + enum arm_smmu_domain_stage stage; + bool non_strict; + 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; + u64 attributes; + u32 secure_vmid; + struct list_head pte_info_list; + struct list_head unassign_list; + struct mutex assign_lock; + struct list_head secure_pool_list; + /* nonsecure pool protected by pgtbl_lock */ + struct list_head nonsecure_pool; + struct msm_iommu_domain domain; +}; + /* Implementation details, yay! */ struct arm_smmu_impl { @@ -375,6 +424,7 @@ struct arm_smmu_impl { u64 val); int (*cfg_probe)(struct arm_smmu_device *smmu); int (*reset)(struct arm_smmu_device *smmu); + int (*init_context)(struct arm_smmu_domain *smmu_domain); }; static inline void __iomem *arm_smmu_page(struct arm_smmu_device *smmu, int n)