From b1cce515d160651a4ae6cb7046b3f9b77cabdcfa Mon Sep 17 00:00:00 2001 From: Qingqing Zhou Date: Fri, 9 Aug 2019 11:13:22 +0530 Subject: [PATCH] arm: dma-mapping: Use default iommu domain When a device probes and arch_setup_dma_ops is called, check if the device has an associated default iommu_domain. If it does, setup a virtual address allocator of the type described by the default domain's attributes. Mark arm_iommu_create_mapping and arm_iommu_attach_device as deprecated, since clients will no longer need to use them now that the iommu framework will associate them with a default domain. Change-Id: I0721c038c68f0f48b78d50f9a72418dbaed97172 Signed-off-by: Vijayanand Jitta Signed-off-by: Qingqing Zhou --- arch/arm/mm/dma-mapping.c | 150 +++++++++++++++++++++++++++++++++++--- 1 file changed, 138 insertions(+), 12 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index c31c72c200ee..53ccb0dbb75c 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2056,6 +2056,7 @@ const struct dma_map_ops iommu_coherent_ops = { }; /** + * DEPRECATED * arm_iommu_create_mapping * @bus: pointer to the bus holding the client device (for IOMMU calls) * @base: start address of the valid IO address space @@ -2158,6 +2159,9 @@ static int extend_iommu_mapping(struct dma_iommu_mapping *mapping) return 0; } +/** + * DEPRECATED + */ void arm_iommu_release_mapping(struct dma_iommu_mapping *mapping) { if (mapping) @@ -2182,6 +2186,7 @@ static int __arm_iommu_attach_device(struct device *dev, } /** + * DEPRECATED * arm_iommu_attach_device * @dev: valid struct device pointer * @mapping: io address space mapping structure (returned from @@ -2209,6 +2214,7 @@ int arm_iommu_attach_device(struct device *dev, EXPORT_SYMBOL_GPL(arm_iommu_attach_device); /** + * DEPRECATED * arm_iommu_detach_device * @dev: valid struct device pointer * @@ -2239,42 +2245,162 @@ static const struct dma_map_ops *arm_get_iommu_dma_map_ops(bool coherent) return coherent ? &iommu_coherent_ops : &iommu_ops; } +static void arm_iommu_dma_release_mapping(struct kref *kref) +{ + int i; + struct dma_iommu_mapping *mapping = + container_of(kref, struct dma_iommu_mapping, kref); + + for (i = 0; i < mapping->nr_bitmaps; i++) + kfree(mapping->bitmaps[i]); + kfree(mapping->bitmaps); + kfree(mapping); +} + +struct dma_iommu_mapping * +arm_iommu_dma_init_mapping(dma_addr_t base, u64 size) +{ + unsigned int bits = size >> PAGE_SHIFT; + unsigned int bitmap_size = BITS_TO_LONGS(bits) * sizeof(long); + struct dma_iommu_mapping *mapping; + int extensions = 1; + int err = -ENOMEM; + + /* currently only 32-bit DMA address space is supported */ + if (size > DMA_BIT_MASK(32) + 1) + return ERR_PTR(-ERANGE); + + if (!bitmap_size) + return ERR_PTR(-EINVAL); + + if (bitmap_size > PAGE_SIZE) { + extensions = bitmap_size / PAGE_SIZE; + bitmap_size = PAGE_SIZE; + } + + mapping = kzalloc(sizeof(struct dma_iommu_mapping), GFP_KERNEL); + if (!mapping) + goto err; + + mapping->bitmap_size = bitmap_size; + mapping->bitmaps = kcalloc(extensions, sizeof(unsigned long *), + GFP_KERNEL); + if (!mapping->bitmaps) + goto err2; + + mapping->bitmaps[0] = kzalloc(bitmap_size, GFP_KERNEL); + if (!mapping->bitmaps[0]) + goto err3; + + mapping->nr_bitmaps = 1; + mapping->extensions = extensions; + mapping->base = base; + mapping->bits = BITS_PER_BYTE * bitmap_size; + + spin_lock_init(&mapping->lock); + + kref_init(&mapping->kref); + return mapping; +err3: + kfree(mapping->bitmaps); +err2: + kfree(mapping); +err: + return ERR_PTR(err); +} + +/* + * Checks for "qcom,iommu-dma-addr-pool" property. + * If not present, leaves dma_addr and dma_size unmodified. + */ +static void arm_iommu_get_dma_window(struct device *dev, u64 *dma_addr, + u64 *dma_size) +{ + struct device_node *np; + int naddr, nsize, len; + const __be32 *ranges; + + if (!dev->of_node) + return; + + np = of_parse_phandle(dev->of_node, "qcom,iommu-group", 0); + if (!np) + np = dev->of_node; + + ranges = of_get_property(np, "qcom,iommu-dma-addr-pool", &len); + if (!ranges) + return; + + len /= sizeof(u32); + naddr = of_n_addr_cells(np); + nsize = of_n_size_cells(np); + if (len < naddr + nsize) { + dev_err(dev, "Invalid length for qcom,iommu-dma-addr-pool, expected %d cells\n", + naddr + nsize); + return; + } + if (naddr == 0 || nsize == 0) { + dev_err(dev, "Invalid #address-cells %d or #size-cells %d\n", + naddr, nsize); + return; + } + + *dma_addr = of_read_number(ranges, naddr); + *dma_size = of_read_number(ranges + naddr, nsize); +} + static bool arm_setup_iommu_dma_ops(struct device *dev, u64 dma_base, u64 size, const struct iommu_ops *iommu) { + struct iommu_group *group; + struct iommu_domain *domain; struct dma_iommu_mapping *mapping; if (!iommu) return false; - mapping = arm_iommu_create_mapping(dev->bus, dma_base, size); + group = dev->iommu_group; + if (!group) + return false; + + domain = iommu_get_domain_for_dev(dev); + if (!domain) + return false; + + arm_iommu_get_dma_window(dev, &dma_base, &size); + + /* Allow iommu-debug to call arch_setup_dma_ops to reconfigure itself */ + if (domain->type != IOMMU_DOMAIN_DMA && + !of_device_is_compatible(dev->of_node, "iommu-debug-test")) { + dev_err(dev, "Invalid iommu domain type!\n"); + return false; + } + + mapping = arm_iommu_dma_init_mapping(dma_base, size); if (IS_ERR(mapping)) { - pr_warn("Failed to create %llu-byte IOMMU mapping for device %s\n", + pr_warn("Failed to initialize %llu-byte IOMMU mapping for device %s\n", size, dev_name(dev)); return false; } - if (__arm_iommu_attach_device(dev, mapping)) { - pr_warn("Failed to attached device %s to IOMMU_mapping\n", - dev_name(dev)); - arm_iommu_release_mapping(mapping); - return false; - } + mapping->domain = domain; + kref_get(&mapping->kref); + to_dma_iommu_mapping(dev) = mapping; return true; } static void arm_teardown_iommu_dma_ops(struct device *dev) { - struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); + struct dma_iommu_mapping *mapping; + mapping = to_dma_iommu_mapping(dev); if (!mapping) return; - arm_iommu_detach_device(dev); - arm_iommu_release_mapping(mapping); + kref_put(&mapping->kref, arm_iommu_dma_release_mapping); + to_dma_iommu_mapping(dev) = NULL; } - #else static bool arm_setup_iommu_dma_ops(struct device *dev, u64 dma_base, u64 size,