From: Lu Baolu [mailto:baolu.lu@xxxxxxxxxxxxxxx]
Sent: Monday, September 23, 2019 8:25 PM
If a dmar domain has DOMAIN_FLAG_FIRST_LEVEL_TRANS bit set
in its flags, IOMMU will use the first level page table for
translation. Hence, we need to map or unmap addresses in the
first level page table.
Cc: Ashok Raj <ashok.raj@xxxxxxxxx>
Cc: Jacob Pan <jacob.jun.pan@xxxxxxxxxxxxxxx>
Cc: Kevin Tian <kevin.tian@xxxxxxxxx>
Cc: Liu Yi L <yi.l.liu@xxxxxxxxx>
Cc: Yi Sun <yi.y.sun@xxxxxxxxxxxxxxx>
Signed-off-by: Lu Baolu <baolu.lu@xxxxxxxxxxxxxxx>
---
drivers/iommu/intel-iommu.c | 94 ++++++++++++++++++++++++++++++++-
----
1 file changed, 82 insertions(+), 12 deletions(-)
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 9cfe8098d993..103480016010 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -168,6 +168,11 @@ static inline unsigned long virt_to_dma_pfn(void
*p)
return page_to_dma_pfn(virt_to_page(p));
}
+static inline unsigned long dma_pfn_to_addr(unsigned long pfn)
+{
+ return pfn << VTD_PAGE_SHIFT;
+}
+
/* global iommu list, set NULL for ignored DMAR units */
static struct intel_iommu **g_iommus;
@@ -307,6 +312,9 @@ static int hw_pass_through = 1;
*/
#define DOMAIN_FLAG_LOSE_CHILDREN BIT(1)
+/* Domain uses first level translation for DMA remapping. */
+#define DOMAIN_FLAG_FIRST_LEVEL_TRANS BIT(2)
+
#define for_each_domain_iommu(idx, domain) \
for (idx = 0; idx < g_num_of_iommus; idx++) \
if (domain->iommu_refcnt[idx])
@@ -552,6 +560,11 @@ static inline int domain_type_is_si(struct
dmar_domain *domain)
return domain->flags & DOMAIN_FLAG_STATIC_IDENTITY;
}
+static inline int domain_type_is_flt(struct dmar_domain *domain)
+{
+ return domain->flags & DOMAIN_FLAG_FIRST_LEVEL_TRANS;
+}
+
static inline int domain_pfn_supported(struct dmar_domain *domain,
unsigned long pfn)
{
@@ -1147,8 +1160,15 @@ static struct page *domain_unmap(struct
dmar_domain *domain,
BUG_ON(start_pfn > last_pfn);
/* we don't need lock here; nobody else touches the iova range */
- freelist = dma_pte_clear_level(domain, agaw_to_level(domain-
agaw),- domain->pgd, 0, start_pfn, last_pfn,
NULL);
+ if (domain_type_is_flt(domain))
+ freelist = intel_mmunmap_range(domain,
+ dma_pfn_to_addr(start_pfn),
+ dma_pfn_to_addr(last_pfn + 1));
+ else
+ freelist = dma_pte_clear_level(domain,
+ agaw_to_level(domain->agaw),
+ domain->pgd, 0, start_pfn,
+ last_pfn, NULL);
what about providing an unified interface at the caller side, then having
the level differentiated within the interface?