Loading drivers/iommu/io-pgtable-arm.c +66 −12 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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: *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); return ((phys_addr_t)iopte_to_pfn(pte,data) << data->pg_shift) | iova; 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) Loading Loading @@ -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; Loading drivers/iommu/io-pgtable.h +3 −0 Original line number Diff line number Diff line Loading @@ -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); }; /** Loading Loading
drivers/iommu/io-pgtable-arm.c +66 −12 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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: *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); return ((phys_addr_t)iopte_to_pfn(pte,data) << data->pg_shift) | iova; 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) Loading Loading @@ -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; Loading
drivers/iommu/io-pgtable.h +3 −0 Original line number Diff line number Diff line Loading @@ -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); }; /** Loading