diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2017-01-09 23:59:45 -0800 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-01-09 23:59:45 -0800 |
| commit | 09d27a060b31df6cf116a5f786126bf3d8e00860 (patch) | |
| tree | 2e428ef704429a396dda1ef67d61e77cf52caca4 | |
| parent | 8787746ecb9603c24c3f084d5c31f821b12dc4fd (diff) | |
| parent | f18f48d761000ae252cfdc59a4c9eba68d58394b (diff) | |
Merge "iommu/io-pgtable-arm: support querying IOVA coherency"
| -rw-r--r-- | drivers/iommu/io-pgtable-arm.c | 78 | ||||
| -rw-r--r-- | drivers/iommu/io-pgtable.h | 3 |
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); + }; /** |
