iommu/io-pgtable-arm: support querying IOVA coherency
Support querying whether an IOVA is mapped as coherent.
Change-Id: Ie37b7f0bfa4038ee06e50104da3747a1257c0813
Signed-off-by: Liam Mark <lmark@codeaurora.org>
Signed-off-by: Patrick Daly <pdaly@codeaurora.org>
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 3408d7c..6222acd 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -109,6 +109,7 @@
#define ARM_LPAE_PTE_AP_RW (((arm_lpae_iopte)1) << 6)
#define ARM_LPAE_PTE_AP_PRIV_RO (((arm_lpae_iopte)2) << 6)
#define ARM_LPAE_PTE_AP_RO (((arm_lpae_iopte)3) << 6)
+#define ARM_LPAE_PTE_ATTRINDX_MASK 0x7
#define ARM_LPAE_PTE_ATTRINDX_SHIFT 2
#define ARM_LPAE_PTE_nG (((arm_lpae_iopte)1) << 11)
@@ -808,39 +809,91 @@
return unmapped;
}
-static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
- unsigned long iova)
+static int arm_lpae_iova_to_pte(struct arm_lpae_io_pgtable *data,
+ unsigned long iova, int *plvl_ret,
+ arm_lpae_iopte *ptep_ret)
{
- struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
arm_lpae_iopte pte, *ptep = data->pgd;
- int lvl = ARM_LPAE_START_LVL(data);
+ *plvl_ret = ARM_LPAE_START_LVL(data);
+ *ptep_ret = 0;
do {
/* Valid IOPTE pointer? */
if (!ptep)
- return 0;
+ return -EINVAL;
/* Grab the IOPTE we're interested in */
- pte = *(ptep + ARM_LPAE_LVL_IDX(iova, lvl, data));
+ pte = *(ptep + ARM_LPAE_LVL_IDX(iova, *plvl_ret, data));
/* Valid entry? */
if (!pte)
- return 0;
+ return -EINVAL;
/* Leaf entry? */
- if (iopte_leaf(pte,lvl))
+ if (iopte_leaf(pte, *plvl_ret))
goto found_translation;
/* Take it to the next level */
ptep = iopte_deref(pte, data);
- } while (++lvl < ARM_LPAE_MAX_LEVELS);
+ } while (++(*plvl_ret) < ARM_LPAE_MAX_LEVELS);
/* Ran out of page tables to walk */
- return 0;
+ return -EINVAL;
found_translation:
- iova &= (ARM_LPAE_BLOCK_SIZE(lvl, data) - 1);
- return ((phys_addr_t)iopte_to_pfn(pte,data) << data->pg_shift) | iova;
+ *ptep_ret = pte;
+ return 0;
+}
+
+static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
+ unsigned long iova)
+{
+ struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
+ arm_lpae_iopte pte;
+ int lvl;
+ phys_addr_t phys = 0;
+
+ if (!arm_lpae_iova_to_pte(data, iova, &lvl, &pte)) {
+ iova &= ((1 << ARM_LPAE_LVL_SHIFT(lvl, data)) - 1);
+ phys = ((phys_addr_t)iopte_to_pfn(pte, data)
+ << data->pg_shift) | iova;
+ }
+
+ return phys;
+}
+
+static bool __arm_lpae_is_iova_coherent(struct arm_lpae_io_pgtable *data,
+ arm_lpae_iopte *ptep)
+{
+ if (data->iop.fmt == ARM_64_LPAE_S1 ||
+ data->iop.fmt == ARM_32_LPAE_S1) {
+ int attr_idx = (*ptep & (ARM_LPAE_PTE_ATTRINDX_MASK <<
+ ARM_LPAE_PTE_ATTRINDX_SHIFT)) >>
+ ARM_LPAE_PTE_ATTRINDX_SHIFT;
+ if ((attr_idx == ARM_LPAE_MAIR_ATTR_IDX_CACHE) &&
+ ((*ptep & ARM_LPAE_PTE_SH_IS) ||
+ (*ptep & ARM_LPAE_PTE_SH_OS)))
+ return true;
+ } else {
+ if (*ptep & ARM_LPAE_PTE_MEMATTR_OIWB)
+ return true;
+ }
+
+ return false;
+}
+
+static bool arm_lpae_is_iova_coherent(struct io_pgtable_ops *ops,
+ unsigned long iova)
+{
+ struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
+ arm_lpae_iopte pte;
+ int lvl;
+ bool ret = false;
+
+ if (!arm_lpae_iova_to_pte(data, iova, &lvl, &pte))
+ ret = __arm_lpae_is_iova_coherent(data, &pte);
+
+ return ret;
}
static void arm_lpae_restrict_pgsizes(struct io_pgtable_cfg *cfg)
@@ -920,6 +973,7 @@
.map_sg = arm_lpae_map_sg,
.unmap = arm_lpae_unmap,
.iova_to_phys = arm_lpae_iova_to_phys,
+ .is_iova_coherent = arm_lpae_is_iova_coherent,
};
return data;