summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitchel Humpherys <mitchelh@codeaurora.org>2014-11-11 17:08:57 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:11:21 -0700
commit487d06ca4d9b73be3908e9b3ad5bf111483402d7 (patch)
treed0016f56bedcfb69771e2ca5432c70438e524792
parent57c7610235e8c40a20ac195b98401a1ea09b2318 (diff)
iommu/arm-smmu: support mapping to unattached domains
According to the IOMMU API, a domain doesn't necessarily need to be attached to an IOMMU device before setting up mappings on that domain. However, in the ARM SMMU driver we currently make the assumption that the domain has already been attached to an SMMU. We use this assumption for the following information: (1) To know whether this is a stage1 or stage2 mapping (2) To know whether we need to do an invalidate (ARM_SMMU_OPT_INVALIDATE_ON_MAP) (3) Address sanity checks based on the stage-1 and stage-2 sizes (which are determined by the SMMU configuration) (4) To call the correct cache maintenance routines based on the SMMU device pointer Handle (1) by assuming that the mapping is stage1 by default. If there's an SMMU attached we can check if this is a stage1 or stage2 mapping. This works for us at the moment because no one is setting up stage2 mappings in the kernel. Handle (2) by not invalidating on map if the domain isn't attached to an SMMU since we shouldn't have anything to invalidate in that case anyways. Handle (3) by skipping the sanity checks for unattached domains. Handle (4) by using the low-level caching APIs. Currently we don't even free up the context bank when the device is detached, presumably so that we don't have to set it up again on re-attach. This limits the number of domains that can be attached to an SMMU, even if they detach from it before the next one attaches. Remove this 1-to-1 domain-to-SMMU assumption by freeing up the "domain context" (context bank allocation, etc) on detach. CRs-Fixed: 752812 Change-Id: I990a11ed52381ce4ecea0f82b4c6e2d54e08f2d6 Signed-off-by: Mitchel Humpherys <mitchelh@codeaurora.org>
-rw-r--r--drivers/iommu/arm-smmu.c56
1 files changed, 32 insertions, 24 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 0854a3057957..8c1f479ddf58 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -877,7 +877,6 @@ static void arm_smmu_flush_pgtable(struct arm_smmu_domain *smmu_domain,
int coherent_htw_disable = smmu_domain->attributes &
(1 << DOMAIN_ATTR_COHERENT_HTW_DISABLE);
-
/* Ensure new page tables are visible to the hardware walker */
if (!coherent_htw_disable) {
dsb(ishst);
@@ -889,8 +888,11 @@ static void arm_smmu_flush_pgtable(struct arm_smmu_domain *smmu_domain,
* recursion here as the SMMU table walker will not be wired
* through another SMMU.
*/
- dma_map_page(smmu->dev, virt_to_page(addr), offset, size,
- DMA_TO_DEVICE);
+ if (smmu)
+ dma_map_page(smmu->dev, virt_to_page(addr),
+ offset, size, DMA_TO_DEVICE);
+ else
+ dmac_clean_range(addr, addr + size);
}
}
@@ -1156,6 +1158,7 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
}
__arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
+ smmu_domain->smmu = NULL;
}
static int arm_smmu_domain_init(struct iommu_domain *domain)
@@ -1257,7 +1260,6 @@ static void arm_smmu_domain_destroy(struct iommu_domain *domain)
* Free the domain resources. We assume that all devices have
* already been detached.
*/
- arm_smmu_destroy_domain_context(domain);
arm_smmu_free_pgtables(smmu_domain);
kfree(smmu_domain);
}
@@ -1504,6 +1506,7 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
dev->archdata.iommu = NULL;
arm_smmu_domain_remove_master(smmu_domain, cfg);
+ arm_smmu_destroy_domain_context(domain);
mutex_lock(&smmu->attach_lock);
if (!--smmu->attach_count)
arm_smmu_power_off(smmu);
@@ -1700,22 +1703,31 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
unsigned long iova, phys_addr_t paddr,
size_t size, int prot)
{
- int ret, stage;
+ int ret, stage = 1;
unsigned long end;
- phys_addr_t input_mask, output_mask;
struct arm_smmu_device *smmu = smmu_domain->smmu;
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
pgd_t *pgd = cfg->pgd;
unsigned long flags;
- if (cfg->cbar == CBAR_TYPE_S2_TRANS) {
- stage = 2;
- input_mask = (1ULL << smmu->s2_input_size) - 1;
- output_mask = (1ULL << smmu->s2_output_size) - 1;
- } else {
- stage = 1;
- input_mask = (1ULL << smmu->s1_input_size) - 1;
- output_mask = (1ULL << smmu->s1_output_size) - 1;
+ /* some extra sanity checks for attached domains */
+ if (smmu) {
+ phys_addr_t input_mask, output_mask;
+
+ if (cfg->cbar == CBAR_TYPE_S2_TRANS) {
+ stage = 2;
+ input_mask = (1ULL << smmu->s2_input_size) - 1;
+ output_mask = (1ULL << smmu->s2_output_size) - 1;
+ } else {
+ input_mask = (1ULL << smmu->s1_input_size) - 1;
+ output_mask = (1ULL << smmu->s1_output_size) - 1;
+ }
+
+ if ((phys_addr_t)iova & ~input_mask)
+ return -ERANGE;
+
+ if (paddr & ~output_mask)
+ return -ERANGE;
}
if (!pgd)
@@ -1724,12 +1736,6 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
if (size & ~PAGE_MASK)
return -EINVAL;
- if ((phys_addr_t)iova & ~input_mask)
- return -ERANGE;
-
- if (paddr & ~output_mask)
- return -ERANGE;
-
spin_lock_irqsave(&smmu_domain->lock, flags);
pgd += pgd_index(iova);
end = iova + size;
@@ -1762,7 +1768,7 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
ret = arm_smmu_handle_mapping(smmu_domain, iova, paddr, size, prot);
- if (!ret &&
+ if (!ret && smmu_domain->smmu &&
(smmu_domain->smmu->options & ARM_SMMU_OPT_INVALIDATE_ON_MAP)) {
arm_smmu_enable_clocks(smmu_domain->smmu);
arm_smmu_tlb_inv_context(smmu_domain);
@@ -1779,9 +1785,11 @@ static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
struct arm_smmu_domain *smmu_domain = domain->priv;
ret = arm_smmu_handle_mapping(smmu_domain, iova, 0, size, 0);
- arm_smmu_enable_clocks(smmu_domain->smmu);
- arm_smmu_tlb_inv_context(smmu_domain);
- arm_smmu_disable_clocks(smmu_domain->smmu);
+ if (smmu_domain->smmu) {
+ arm_smmu_enable_clocks(smmu_domain->smmu);
+ arm_smmu_tlb_inv_context(smmu_domain);
+ arm_smmu_disable_clocks(smmu_domain->smmu);
+ }
return ret ? 0 : size;
}