intel-iommu: iova_to_phys: fill in bits from iova when large pte

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



intel_iommu_iova_to_phys returns incorrect physical address 
when iova is translated by large pte.

Fill in bits from iova when creating the physical address.

Signed-off-by: Marcelo Tosatti <mtosatti@xxxxxxxxxx>

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 15e9b57..f8f2988 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -774,7 +774,8 @@ out:
 }
 
 static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
-				      unsigned long pfn, int target_level)
+				      unsigned long pfn, int target_level,
+				      int *large_page)
 {
 	int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
 	struct dma_pte *parent, *pte = NULL;
@@ -790,8 +791,15 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
 
 		offset = pfn_level_offset(pfn, level);
 		pte = &parent[offset];
-		if (!target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte)))
-			break;
+		if (!target_level) {
+			if (!dma_pte_present(pte))
+				break;
+			if (dma_pte_superpage(pte)) {
+				if (large_page)
+					*large_page = level;
+				break;
+			}
+		}
 		if (level == target_level)
 			break;
 
@@ -1824,7 +1832,8 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
 		if (!pte) {
 			largepage_lvl = hardware_largepage_caps(domain, iov_pfn, phys_pfn, sg_res);
 
-			first_pte = pte = pfn_to_dma_pte(domain, iov_pfn, largepage_lvl);
+			first_pte = pte = pfn_to_dma_pte(domain, iov_pfn,
+							 largepage_lvl, NULL);
 			if (!pte)
 				return -ENOMEM;
 			/* It is large page*/
@@ -4129,11 +4138,17 @@ static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
 {
 	struct dmar_domain *dmar_domain = domain->priv;
 	struct dma_pte *pte;
-	u64 phys = 0;
+	u64 phys = 0, iova_mask;
+	int large_page = 1;
 
-	pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, 0);
-	if (pte)
+	pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, 0,
+			     &large_page);
+	if (pte) {
 		phys = dma_pte_addr(pte);
+		large_page--;
+		iova_mask = (1ULL << (VTD_PAGE_SHIFT+(large_page*9)))-1;
+		phys |= iova & iova_mask;
+	}
 
 	return phys;
 }
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux