summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wright <chrisw@sous-sol.org>2011-05-28 13:15:02 -0500
committerDavid Woodhouse <David.Woodhouse@intel.com>2011-06-01 12:47:34 +0100
commit8fcc5372fbac085199d84a880503ed67aba3fe49 (patch)
treeaab84e5ac18fa8028efa49e2cc47a5b97d873916
parent9b4554b21ed07e8556405510638171f0c787742a (diff)
downloadlinux-3.10-8fcc5372fbac085199d84a880503ed67aba3fe49.tar.gz
linux-3.10-8fcc5372fbac085199d84a880503ed67aba3fe49.tar.bz2
linux-3.10-8fcc5372fbac085199d84a880503ed67aba3fe49.zip
intel-iommu: Check for identity mapping candidate using system dma mask
The identity mapping code appears to make the assumption that if the devices dma_mask is greater than 32bits the device can use identity mapping. But that is not true: take the case where we have a 40bit device in a 44bit architecture. The device can potentially receive a physical address that it will truncate and cause incorrect addresses to be used. Instead check to see if the device's dma_mask is large enough to address the system's dma_mask. Signed-off-by: Mike Travis <travis@sgi.com> Reviewed-by: Mike Habeck <habeck@sgi.com> Cc: stable@kernel.org Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/pci/intel-iommu.c15
1 files changed, 13 insertions, 2 deletions
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index 4eaec2fa136..dcf051d53b7 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -2316,8 +2316,19 @@ static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
* Assume that they will -- if they turn out not to be, then we can
* take them out of the 1:1 domain later.
*/
- if (!startup)
- return pdev->dma_mask > DMA_BIT_MASK(32);
+ if (!startup) {
+ /*
+ * If the device's dma_mask is less than the system's memory
+ * size then this is not a candidate for identity mapping.
+ */
+ u64 dma_mask = pdev->dma_mask;
+
+ if (pdev->dev.coherent_dma_mask &&
+ pdev->dev.coherent_dma_mask < dma_mask)
+ dma_mask = pdev->dev.coherent_dma_mask;
+
+ return dma_mask >= dma_get_required_mask(&pdev->dev);
+ }
return 1;
}