summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2017-01-09 23:59:45 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2017-01-09 23:59:45 -0800
commit09d27a060b31df6cf116a5f786126bf3d8e00860 (patch)
tree2e428ef704429a396dda1ef67d61e77cf52caca4
parent8787746ecb9603c24c3f084d5c31f821b12dc4fd (diff)
parentf18f48d761000ae252cfdc59a4c9eba68d58394b (diff)
Merge "iommu/io-pgtable-arm: support querying IOVA coherency"
-rw-r--r--drivers/iommu/io-pgtable-arm.c78
-rw-r--r--drivers/iommu/io-pgtable.h3
2 files changed, 69 insertions, 12 deletions
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 1cd1a18dd037..3f9825a9addb 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)
@@ -813,39 +814,91 @@ static size_t arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova,
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 &= ((1 << ARM_LPAE_LVL_SHIFT(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)
@@ -925,6 +978,7 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg)
.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;
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
index 1d92edbd5ed1..32ae3c59fe7c 100644
--- a/drivers/iommu/io-pgtable.h
+++ b/drivers/iommu/io-pgtable.h
@@ -114,6 +114,9 @@ struct io_pgtable_ops {
size_t size);
phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops,
unsigned long iova);
+ bool (*is_iova_coherent)(struct io_pgtable_ops *ops,
+ unsigned long iova);
+
};
/**