iommu: msm: validate subsequent lazy map arguments

Validate that attempts to lazy map a buffer, that is already
lazy mapped, request the same number of elements, direction
and dma mapping attributes as the original mapping.

Change-Id: I6094cd6281a22525aa9971f1c5684f423be08580
Signed-off-by: Liam Mark <lmark@codeaurora.org>
diff --git a/drivers/iommu/msm_dma_iommu_mapping.c b/drivers/iommu/msm_dma_iommu_mapping.c
index 07e5236..3f739a2 100644
--- a/drivers/iommu/msm_dma_iommu_mapping.c
+++ b/drivers/iommu/msm_dma_iommu_mapping.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -27,9 +27,11 @@
  * @dev - Device this is mapped to. Used as key
  * @sgl - The scatterlist for this mapping
  * @nents - Number of entries in sgl
- * @dir - The direction for the unmap.
+ * @dir - The direction for the map.
  * @meta - Backpointer to the meta this guy belongs to.
  * @ref - for reference counting this mapping
+ * @map_attrs - dma mapping attributes
+ * @buf_start_addr - address of start of buffer
  *
  * Represents a mapping of one dma_buf buffer to a particular device
  * and address range. There may exist other mappings of this buffer in
@@ -44,6 +46,8 @@
 	enum dma_data_direction dir;
 	struct msm_iommu_meta *meta;
 	struct kref ref;
+	unsigned long map_attrs;
+	dma_addr_t buf_start_addr;
 };
 
 struct msm_iommu_meta {
@@ -199,21 +203,43 @@
 		iommu_map->sgl.dma_address = sg->dma_address;
 		iommu_map->sgl.dma_length = sg->dma_length;
 		iommu_map->dev = dev;
+		iommu_map->dir = dir;
+		iommu_map->nents = nents;
+		iommu_map->map_attrs = attrs;
+		iommu_map->buf_start_addr = sg_phys(sg);
 		msm_iommu_add(iommu_meta, iommu_map);
 
 	} else {
-		sg->dma_address = iommu_map->sgl.dma_address;
-		sg->dma_length = iommu_map->sgl.dma_length;
+		if (nents == iommu_map->nents &&
+		    dir == iommu_map->dir &&
+		    attrs == iommu_map->map_attrs &&
+		    sg_phys(sg) == iommu_map->buf_start_addr) {
+			sg->dma_address = iommu_map->sgl.dma_address;
+			sg->dma_length = iommu_map->sgl.dma_length;
 
-		kref_get(&iommu_map->ref);
-		if (is_device_dma_coherent(dev))
-			/*
-			 * Ensure all outstanding changes for coherent
-			 * buffers are applied to the cache before any
-			 * DMA occurs.
-			 */
-			dmb(ish);
-		ret = nents;
+			kref_get(&iommu_map->ref);
+			if (is_device_dma_coherent(dev))
+				/*
+				 * Ensure all outstanding changes for coherent
+				 * buffers are applied to the cache before any
+				 * DMA occurs.
+				 */
+				dmb(ish);
+			ret = nents;
+		} else {
+			bool start_diff = (sg_phys(sg) !=
+					   iommu_map->buf_start_addr);
+
+			dev_err(dev, "lazy map request differs:\n"
+				"req dir:%d, original dir:%d\n"
+				"req nents:%d, original nents:%d\n"
+				"req map attrs:%lu, original map attrs:%lu\n"
+				"req buffer start address differs:%d\n",
+				dir, iommu_map->dir, nents,
+				iommu_map->nents, attrs, iommu_map->map_attrs,
+				start_diff);
+			ret = -EINVAL;
+		}
 	}
 	mutex_unlock(&iommu_meta->lock);
 	return ret;
@@ -321,13 +347,9 @@
 		goto out;
 	}
 
-	/*
-	 * Save direction for later use when we actually unmap.
-	 * Not used right now but in the future if we go to coherent mapping
-	 * API we might want to call the appropriate API when client asks
-	 * to unmap
-	 */
-	iommu_map->dir = dir;
+	if (dir != iommu_map->dir)
+		WARN(1, "%s: (%pK) dir:%d differs from original dir:%d\n",
+		     __func__, dma_buf, dir, iommu_map->dir);
 
 	kref_put(&iommu_map->ref, msm_iommu_map_release);
 	mutex_unlock(&meta->lock);