[PATCH 3/5] iommu/io-pgtable-arm-v7s: Hook up map_sg()

Isaac J. Manjarres
2021-01-09T01:51:50Z

Implement the map_sg io-pgtable op for the ARMv7s io-pgtable
code, so that IOMMU drivers can call it when they need to map
a scatter-gather list.

Signed-off-by: Isaac J. Manjarres <isaacm@codeaurora.org>
---
drivers/iommu/io-pgtable-arm-v7s.c | 90 ++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)

diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c
index 1d92ac9..40d96d2 100644
--- a/drivers/iommu/io-pgtable-arm-v7s.c
+++ b/drivers/iommu/io-pgtable-arm-v7s.c
@@ -545,6 +545,95 @@ static int arm_v7s_map(struct io_pgtable_ops *ops, unsigned long iova,
return ret;
}

+static int arm_v7s_map_by_pgsize(struct io_pgtable_ops *ops,
+ unsigned long iova, phys_addr_t paddr,
+ size_t size, int prot, gfp_t gfp,
+ size_t *mapped)
+{
+ struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops);
+ struct io_pgtable *iop = &data->iop;
+ struct io_pgtable_cfg *cfg = &iop->cfg;
+ unsigned int min_pagesz = 1 << __ffs(cfg->pgsize_bitmap);
+ int ret;
+ size_t pgsize;
+
+ if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
+ pr_err("unaligned: iova 0x%lx pa %pa size 0x%zx min_pagesz 0x%x\n",
+ iova, &paddr, size, min_pagesz);
+ return -EINVAL;
+ }
+
+ if (WARN_ON((iova + size) >= (1ULL << cfg->ias) ||
+ (paddr + size) >= (1ULL << cfg->oas)))
+ return -ERANGE;
+
+ while (size) {
+ pgsize = iommu_pgsize(cfg->pgsize_bitmap, iova | paddr, size);
+ ret = __arm_v7s_map(data, iova, paddr, pgsize, prot, 1,
+ data->pgd, gfp);
+
+ if (iop->cfg.quirks & IO_PGTABLE_QUIRK_TLBI_ON_MAP) {
+ io_pgtable_tlb_flush_walk(&data->iop, iova, size,
+ ARM_V7S_BLOCK_SIZE(2));
+ } else {
+ wmb();
+ }
+
+ if (ret)
+ return ret;
+
+ iova += pgsize;
+ paddr += pgsize;
+ *mapped += pgsize;
+ size -= pgsize;
+ }
+
+ return 0;
+}
+
+static int arm_v7s_map_sg(struct io_pgtable_ops *ops, unsigned long iova,
+ struct scatterlist *sg, unsigned int nents,
+ int iommu_prot, gfp_t gfp, size_t *mapped)
+{
+ size_t len = 0;
+ unsigned int i = 0;
+ int ret;
+ phys_addr_t start;
+
+ *mapped = 0;
+
+ /* If no access, then nothing to do */
+ if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE)))
+ return 0;
+
+ while (i <= nents) {
+ phys_addr_t s_phys = sg_phys(sg);
+
+ if (len && s_phys != start + len) {
+ ret = arm_v7s_map_by_pgsize(ops, iova + *mapped, start,
+ len, iommu_prot, gfp,
+ mapped);
+
+ if (ret)
+ return ret;
+
+ len = 0;
+ }
+
+ if (len) {
+ len += sg->length;
+ } else {
+ len = sg->length;
+ start = s_phys;
+ }
+
+ if (++i < nents)
+ sg = sg_next(sg);
+ }
+
+ return 0;
+}
+
static void arm_v7s_free_pgtable(struct io_pgtable *iop)
{
struct arm_v7s_io_pgtable *data = io_pgtable_to_data(iop);
@@ -783,6 +872,7 @@ static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg,

data->iop.ops = (struct io_pgtable_ops) {
.map = arm_v7s_map,
+ .map_sg = arm_v7s_map_sg,
.unmap = arm_v7s_unmap,
.iova_to_phys = arm_v7s_iova_to_phys,
};