diff options
| author | Mitchel Humpherys <mitchelh@codeaurora.org> | 2014-11-11 17:08:57 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:11:21 -0700 |
| commit | 487d06ca4d9b73be3908e9b3ad5bf111483402d7 (patch) | |
| tree | d0016f56bedcfb69771e2ca5432c70438e524792 | |
| parent | 57c7610235e8c40a20ac195b98401a1ea09b2318 (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.c | 56 |
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; } |
