The legacy unmanaged DMA domain path sets up the IOVA bookkeeping based
on the dma_range_map of the device. This property however is not
necessarily available during the time we create the bookkeeping with
core-owned default domains, and doesn't take into account the device's
DMA mask.

Resolve this by passing the device's DMA limit to __alloc_iova and
refusing allocations that would go above that limit. Allocation logic is
unchanged except for the check.

Signed-off-by: Mikko Perttunen <[email protected]>
---
 arch/arm/mm/dma-mapping.c | 36 ++++++++++++++++++++----------------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index b82cf58f8bc7..3703f9e2c9a2 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -752,7 +752,7 @@ static int __dma_info_to_prot(enum dma_data_direction dir, 
unsigned long attrs)
 static int extend_iommu_mapping(struct dma_iommu_mapping *mapping);
 
 static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping,
-                                     size_t size)
+                                     size_t size, u64 dma_limit)
 {
        unsigned int order = get_order(size);
        unsigned int align = 0;
@@ -776,7 +776,6 @@ static inline dma_addr_t __alloc_iova(struct 
dma_iommu_mapping *mapping,
                if (start >= mapping->bits)
                        continue;
 
-               bitmap_set(mapping->bitmaps[i], start, count);
                break;
        }
 
@@ -786,27 +785,28 @@ static inline dma_addr_t __alloc_iova(struct 
dma_iommu_mapping *mapping,
         * address range of size bytes.
         */
        if (i == mapping->nr_bitmaps) {
-               if (extend_iommu_mapping(mapping)) {
-                       spin_unlock_irqrestore(&mapping->lock, flags);
-                       return DMA_MAPPING_ERROR;
-               }
+               if (extend_iommu_mapping(mapping))
+                       goto fail;
 
                start = bitmap_find_next_zero_area(mapping->bitmaps[i],
                                mapping->bits, 0, count, align);
 
-               if (start >= mapping->bits) {
-                       spin_unlock_irqrestore(&mapping->lock, flags);
-                       return DMA_MAPPING_ERROR;
-               }
-
-               bitmap_set(mapping->bitmaps[i], start, count);
+               if (start >= mapping->bits)
+                       goto fail;
        }
-       spin_unlock_irqrestore(&mapping->lock, flags);
 
        iova = mapping->base + (mapping_size * i);
        iova += start << PAGE_SHIFT;
+       if ((u64)iova + PAGE_ALIGN(size) - 1 > dma_limit)
+               goto fail;
 
+       bitmap_set(mapping->bitmaps[i], start, count);
+       spin_unlock_irqrestore(&mapping->lock, flags);
        return iova;
+
+fail:
+       spin_unlock_irqrestore(&mapping->lock, flags);
+       return DMA_MAPPING_ERROR;
 }
 
 static inline void __free_iova(struct dma_iommu_mapping *mapping,
@@ -965,7 +965,8 @@ __iommu_create_mapping(struct device *dev, struct page 
**pages, size_t size,
        dma_addr_t dma_addr, iova;
        int i;
 
-       dma_addr = __alloc_iova(mapping, size);
+       dma_addr = __alloc_iova(mapping, size,
+                               min_not_zero(dev->coherent_dma_mask, 
dev->bus_dma_limit));
        if (dma_addr == DMA_MAPPING_ERROR)
                return dma_addr;
 
@@ -1194,7 +1195,9 @@ static int __map_sg_chunk(struct device *dev, struct 
scatterlist *sg,
        size = PAGE_ALIGN(size);
        *handle = DMA_MAPPING_ERROR;
 
-       iova_base = iova = __alloc_iova(mapping, size);
+       iova = __alloc_iova(mapping, size,
+                           min_not_zero(dma_get_mask(dev), 
dev->bus_dma_limit));
+       iova_base = iova;
        if (iova == DMA_MAPPING_ERROR)
                return -ENOMEM;
 
@@ -1375,7 +1378,8 @@ static dma_addr_t arm_iommu_map_phys(struct device *dev, 
phys_addr_t phys,
            !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO)))
                arch_sync_dma_for_device(phys, size, dir);
 
-       dma_addr = __alloc_iova(mapping, len);
+       dma_addr = __alloc_iova(mapping, len,
+                               min_not_zero(dma_get_mask(dev), 
dev->bus_dma_limit));
        if (dma_addr == DMA_MAPPING_ERROR)
                return dma_addr;
 

-- 
2.55.0

Reply via email to