* [RFC PATCH] iommu: add ARM short descriptor page table allocator.
@ 2015-04-28 7:41 Yong Wu
2015-05-05 16:18 ` Joerg Roedel
2015-05-12 9:15 ` Matthias Brugger
0 siblings, 2 replies; 8+ messages in thread
From: Yong Wu @ 2015-04-28 7:41 UTC (permalink / raw)
To: Will Deacon, Joerg Roedel
Cc: Matthias Brugger, Robin Murphy, Tomasz Figa, Lucas Stach,
linux-mediatek, Sascha Hauer, srv_heupstream, linux-kernel,
linux-arm-kernel, iommu, Yong Wu
This patch is for ARM Short Descriptor Format.It has 2-levels
pagetable and the allocator supports 4K/64K/1M/16M.
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
drivers/iommu/Kconfig | 7 +
drivers/iommu/Makefile | 1 +
drivers/iommu/io-pgtable-arm-short.c | 489 +++++++++++++++++++++++++++++++++++
drivers/iommu/io-pgtable.c | 4 +
drivers/iommu/io-pgtable.h | 6 +
5 files changed, 507 insertions(+)
create mode 100644 drivers/iommu/io-pgtable-arm-short.c
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index baa0d97..8e50e73 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -38,6 +38,13 @@ config IOMMU_IO_PGTABLE_LPAE_SELFTEST
If unsure, say N here.
+config IOMMU_IO_PGTABLE_SHORT
+ bool "ARMv7/v8 Short Descriptor Format"
+ select IOMMU_IO_PGTABLE
+ help
+ Enable support for the ARM Short descriptor pagetable format.
+ It has 2-levels pagetable and The allocator supports 4K/64K/1M/16M.
+
endmenu
config IOMMU_IOVA
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 080ffab..815b3c8 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_IOMMU_API) += iommu-traces.o
obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
+obj-$(CONFIG_IOMMU_IO_PGTABLE_SHORT) += io-pgtable-arm-short.o
obj-$(CONFIG_IOMMU_IOVA) += iova.o
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
diff --git a/drivers/iommu/io-pgtable-arm-short.c b/drivers/iommu/io-pgtable-arm-short.c
new file mode 100644
index 0000000..8d723ec
--- /dev/null
+++ b/drivers/iommu/io-pgtable-arm-short.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2014-2015 MediaTek Inc.
+ * Author: Yong Wu <yong.wu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "arm-short-desc io-pgtable: "fmt
+
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/iommu.h>
+#include <linux/errno.h>
+
+#include "io-pgtable.h"
+
+typedef u32 arm_short_iopte;
+
+struct arm_short_io_pgtable {
+ struct io_pgtable iop;
+ struct kmem_cache *ptekmem;
+ size_t pgd_size;
+ void *pgd;
+};
+
+#define io_pgtable_short_to_data(x) \
+ container_of((x), struct arm_short_io_pgtable, iop)
+
+#define io_pgtable_ops_to_pgtable(x) \
+ container_of((x), struct io_pgtable, ops)
+
+#define io_pgtable_short_ops_to_data(x) \
+ io_pgtable_short_to_data(io_pgtable_ops_to_pgtable(x))
+
+#define ARM_SHORT_MAX_ADDR_BITS 32
+
+#define ARM_SHORT_PGDIR_SHIFT 20
+#define ARM_SHORT_PAGE_SHIFT 12
+#define ARM_SHORT_PTRS_PER_PTE 256
+#define ARM_SHORT_BYTES_PER_PTE 1024
+
+/* 1 level pagetable */
+#define ARM_SHORT_F_PGD_TYPE_PAGE (0x1)
+#define ARM_SHORT_F_PGD_TYPE_PAGE_MSK (0x3)
+#define ARM_SHORT_F_PGD_TYPE_SECTION (0x2)
+#define ARM_SHORT_F_PGD_TYPE_SUPERSECTION (0x2 | (1 << 18))
+#define ARM_SHORT_F_PGD_TYPE_SECTION_MSK (0x3 | (1 << 18))
+#define ARM_SHORT_F_PGD_TYPE_IS_PAGE(pgd) (((pgd) & 0x3) == 1)
+#define ARM_SHORT_F_PGD_TYPE_IS_SECTION(pgd) \
+ (((pgd) & ARM_SHORT_F_PGD_TYPE_SECTION_MSK) \
+ == ARM_SHORT_F_PGD_TYPE_SECTION)
+#define ARM_SHORT_F_PGD_TYPE_IS_SUPERSECTION(pgd) \
+ (((pgd) & ARM_SHORT_F_PGD_TYPE_SECTION_MSK) \
+ == ARM_SHORT_F_PGD_TYPE_SUPERSECTION)
+
+#define ARM_SHORT_F_PGD_B_BIT BIT(2)
+#define ARM_SHORT_F_PGD_C_BIT BIT(3)
+#define ARM_SHORT_F_PGD_IMPLE_BIT BIT(9)
+#define ARM_SHORT_F_PGD_S_BIT BIT(16)
+#define ARM_SHORT_F_PGD_NG_BIT BIT(17)
+#define ARM_SHORT_F_PGD_NS_BIT_PAGE BIT(3)
+#define ARM_SHORT_F_PGD_NS_BIT_SECTION BIT(19)
+
+#define ARM_SHORT_F_PGD_PA_PAGETABLE_MSK 0xfffffc00
+#define ARM_SHORT_F_PGD_PA_SECTION_MSK 0xfff00000
+#define ARM_SHORT_F_PGD_PA_SUPERSECTION_MSK 0xff000000
+
+/* 2 level pagetable */
+#define ARM_SHORT_F_PTE_TYPE_GET(val) ((val) & 0x3)
+#define ARM_SHORT_F_PTE_TYPE_LARGE BIT(0)
+#define ARM_SHORT_F_PTE_TYPE_SMALL BIT(1)
+#define ARM_SHORT_F_PTE_B_BIT BIT(2)
+#define ARM_SHORT_F_PTE_C_BIT BIT(3)
+#define ARM_SHORT_F_PTE_IMPLE_BIT BIT(9)
+#define ARM_SHORT_F_PTE_S_BIT BIT(10)
+#define ARM_SHORT_F_PTE_PA_LARGE_MSK 0xffff0000
+#define ARM_SHORT_F_PTE_PA_SMALL_MSK 0xfffff000
+
+#define ARM_SHORT_PGD_IDX(a) ((a) >> ARM_SHORT_PGDIR_SHIFT)
+#define ARM_SHORT_PTE_IDX(a) \
+ (((a) >> ARM_SHORT_PAGE_SHIFT) & 0xff)
+#define ARM_SHORT_GET_PTE_VA(pgd) \
+ (phys_to_virt((unsigned long)pgd & ARM_SHORT_F_PGD_PA_PAGETABLE_MSK))
+
+static arm_short_iopte *
+arm_short_get_pte_in_pgd(arm_short_iopte curpgd, unsigned int iova)
+{
+ arm_short_iopte *pte;
+
+ pte = ARM_SHORT_GET_PTE_VA(curpgd);
+ pte += ARM_SHORT_PTE_IDX(iova);
+ return pte;
+}
+
+static arm_short_iopte *
+arm_short_supersection_start(arm_short_iopte *pgd)
+{
+ return (arm_short_iopte *)(round_down((unsigned long)pgd, (16 * 4)));
+}
+
+static int _arm_short_check_free_pte(struct arm_short_io_pgtable *data,
+ arm_short_iopte *pgd)
+{
+ arm_short_iopte *pte;
+ int i;
+
+ pte = ARM_SHORT_GET_PTE_VA(*pgd);
+
+ for (i = 0; i < ARM_SHORT_PTRS_PER_PTE; i++) {
+ if (pte[i] != 0)
+ return 1;
+ }
+
+ /* Free PTE */
+ kmem_cache_free(data->ptekmem, pte);
+ *pgd = 0;
+
+ return 0;
+}
+
+static phys_addr_t arm_short_iova_to_phys(struct io_pgtable_ops *ops,
+ unsigned long iova)
+{
+ struct arm_short_io_pgtable *data = io_pgtable_short_ops_to_data(ops);
+ arm_short_iopte *pte, *pgd = data->pgd;
+ phys_addr_t pa = 0;
+
+ pgd += ARM_SHORT_PGD_IDX(iova);
+
+ if (ARM_SHORT_F_PGD_TYPE_IS_PAGE(*pgd)) {
+ pte = arm_short_get_pte_in_pgd(*pgd, iova);
+
+ if (ARM_SHORT_F_PTE_TYPE_GET(*pte)
+ == ARM_SHORT_F_PTE_TYPE_LARGE) {
+ pa = (*pte) & ARM_SHORT_F_PTE_PA_LARGE_MSK;
+ pa |= iova & (~ARM_SHORT_F_PTE_PA_LARGE_MSK);
+ } else if (ARM_SHORT_F_PTE_TYPE_GET(*pte)
+ == ARM_SHORT_F_PTE_TYPE_SMALL) {
+ pa = (*pte) & ARM_SHORT_F_PTE_PA_SMALL_MSK;
+ pa |= iova & (~ARM_SHORT_F_PTE_PA_SMALL_MSK);
+ }
+ } else {
+ if (ARM_SHORT_F_PGD_TYPE_IS_SECTION(*pgd)) {
+ pa = (*pgd) & ARM_SHORT_F_PGD_PA_SECTION_MSK;
+ pa |= iova & (~ARM_SHORT_F_PGD_PA_SECTION_MSK);
+ } else if (ARM_SHORT_F_PGD_TYPE_IS_SUPERSECTION(*pgd)) {
+ pa = (*pgd) & ARM_SHORT_F_PGD_PA_SUPERSECTION_MSK;
+ pa |= iova & (~ARM_SHORT_F_PGD_PA_SUPERSECTION_MSK);
+ }
+ }
+
+ return pa;
+}
+
+static int arm_short_unmap(struct io_pgtable_ops *ops, unsigned long iova,
+ size_t size)
+{
+ struct arm_short_io_pgtable *data = io_pgtable_short_ops_to_data(ops);
+ arm_short_iopte *pgd;
+ unsigned long iova_start = iova;
+ unsigned long end_plus_1 = (unsigned long)iova + size;
+ const struct iommu_gather_ops *tlb = data->iop.cfg.tlb;
+ void *cookie = data->iop.cookie;
+ int ret;
+
+ do {
+ pgd = (arm_short_iopte *)data->pgd + ARM_SHORT_PGD_IDX(iova);
+
+ if (ARM_SHORT_F_PGD_TYPE_IS_PAGE(*pgd)) {
+ arm_short_iopte *pte;
+ unsigned int pte_offset;
+ unsigned int num_to_clean;
+
+ pte_offset = ARM_SHORT_PTE_IDX(iova);
+ num_to_clean =
+ min((unsigned int)((end_plus_1 - iova) / PAGE_SIZE),
+ (ARM_SHORT_PTRS_PER_PTE - pte_offset));
+
+ pte = arm_short_get_pte_in_pgd(*pgd, iova);
+
+ memset(pte, 0, num_to_clean * sizeof(arm_short_iopte));
+
+ ret = _arm_short_check_free_pte(data, pgd);
+ if (ret == 1)/* pte is not freed, need to flush pte */
+ tlb->flush_pgtable(
+ pte,
+ num_to_clean * sizeof(arm_short_iopte),
+ cookie);
+ else
+ tlb->flush_pgtable(pgd, sizeof(arm_short_iopte),
+ cookie);
+
+ iova += num_to_clean << PAGE_SHIFT;
+ } else if (ARM_SHORT_F_PGD_TYPE_IS_SECTION(*pgd)) {
+ *pgd = 0;
+
+ tlb->flush_pgtable(pgd, sizeof(arm_short_iopte),
+ cookie);
+ iova += SZ_1M;
+ } else if (ARM_SHORT_F_PGD_TYPE_IS_SUPERSECTION(*pgd)) {
+ arm_short_iopte *start;
+
+ start = arm_short_supersection_start(pgd);
+ if (unlikely(start != pgd))
+ pr_warn("%s:suppersection start isn't aligned.iova=0x%lx,pgd=0x%x\n",
+ __func__, iova, *pgd);
+
+ memset(start, 0, 16 * sizeof(arm_short_iopte));
+
+ tlb->flush_pgtable(start, 16 * sizeof(arm_short_iopte),
+ cookie);
+
+ iova = (iova + SZ_16M) & (~(SZ_16M - 1));
+ } else {
+ break;
+ }
+ } while (iova < end_plus_1 && iova);
+
+ tlb->tlb_add_flush(iova_start, size, true, cookie);
+
+ return 0;
+}
+
+static arm_short_iopte __arm_short_pte_port(unsigned int prot, bool large)
+{
+ arm_short_iopte pteprot;
+
+ pteprot = ARM_SHORT_F_PTE_S_BIT;
+
+ pteprot |= large ? ARM_SHORT_F_PTE_TYPE_LARGE :
+ ARM_SHORT_F_PTE_TYPE_SMALL;
+
+ if (prot & IOMMU_CACHE)
+ pteprot |= ARM_SHORT_F_PTE_B_BIT | ARM_SHORT_F_PTE_C_BIT;
+
+ return pteprot;
+}
+
+static arm_short_iopte __arm_short_pgd_port(int prot, bool super)
+{
+ arm_short_iopte pgdprot;
+
+ pgdprot = ARM_SHORT_F_PGD_S_BIT;
+ pgdprot |= super ? ARM_SHORT_F_PGD_TYPE_SUPERSECTION :
+ ARM_SHORT_F_PGD_TYPE_SECTION;
+ if (prot & IOMMU_CACHE)
+ pgdprot |= ARM_SHORT_F_PGD_C_BIT | ARM_SHORT_F_PGD_B_BIT;
+
+ return pgdprot;
+}
+
+static int _arm_short_map_page(struct arm_short_io_pgtable *data,
+ unsigned int iova, phys_addr_t pa,
+ unsigned int prot, bool largepage)
+{
+ arm_short_iopte *pgd = data->pgd;
+ arm_short_iopte *pte;
+ arm_short_iopte pgdprot, pteprot;
+ arm_short_iopte mask = largepage ? ARM_SHORT_F_PTE_PA_LARGE_MSK :
+ ARM_SHORT_F_PTE_PA_SMALL_MSK;
+ int i, ptenum = largepage ? 16 : 1;
+ bool ptenew = false;
+ void *pte_new_va;
+ void *cookie = data->iop.cookie;
+
+ if ((iova | pa) & (~mask)) {
+ pr_err("IOVA|PA Not Aligned(iova=0x%x pa=0x%pa type=%s)\n",
+ iova, &pa, largepage ? "large page" : "small page");
+ return -EINVAL;
+ }
+
+ pgdprot = ARM_SHORT_F_PGD_TYPE_PAGE;
+ if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS)
+ pgdprot |= ARM_SHORT_F_PGD_NS_BIT_PAGE;
+
+ pgd += ARM_SHORT_PGD_IDX(iova);
+
+ if (!(*pgd)) {
+ pte_new_va = kmem_cache_zalloc(data->ptekmem, GFP_KERNEL);
+ if (unlikely(!pte_new_va)) {
+ pr_err("Failed to alloc pte\n");
+ return -ENOMEM;
+ }
+
+ /* Check pte alignment -- must 1K align */
+ if (unlikely((unsigned long)pte_new_va &
+ (ARM_SHORT_BYTES_PER_PTE - 1))) {
+ pr_err("The new pte is not aligned! (va=0x%p)\n",
+ pte_new_va);
+ kmem_cache_free(data->ptekmem, (void *)pte_new_va);
+ return -ENOMEM;
+ }
+ ptenew = true;
+ *pgd = virt_to_phys(pte_new_va) | pgdprot;
+ kmemleak_ignore(pte_new_va);
+ data->iop.cfg.tlb->flush_pgtable(pgd, sizeof(arm_short_iopte),
+ cookie);
+ } else {
+ /* Someone else may have allocated for this pgd */
+ if (((*pgd) & (~ARM_SHORT_F_PGD_PA_PAGETABLE_MSK)) != pgdprot) {
+ pr_err("The prot of old pgd is not Right!iova=0x%x pgd=0x%x pgprot=0x%x\n",
+ iova, (*pgd), pgdprot);
+ return -EEXIST;
+ }
+ }
+
+ pteprot = (arm_short_iopte)pa;
+ pteprot |= __arm_short_pte_port(prot, largepage);
+
+ pte = arm_short_get_pte_in_pgd(*pgd, iova);
+
+ pr_debug("iova:0x%x,pte:0x%p(0x%x),prot:0x%x-%s\n",
+ iova, pte, ARM_SHORT_PTE_IDX(iova), pteprot,
+ largepage ? "large page" : "small page");
+
+ for (i = 0; i < ptenum; i++) {
+ if (pte[i]) {
+ pr_err("The To-Map pte exists!(iova=0x%x pte=0x%x i=%d)\n",
+ iova, pte[i], i);
+ goto err_out;
+ }
+ pte[i] = pteprot;
+ }
+
+ data->iop.cfg.tlb->flush_pgtable(pte, ptenum * sizeof(arm_short_iopte),
+ cookie);
+ return 0;
+
+ err_out:
+ for (i--; i >= 0; i--)
+ pte[i] = 0;
+ if (ptenew)
+ kmem_cache_free(data->ptekmem, pte_new_va);
+ return -EEXIST;
+}
+
+static int _arm_short_map_section(struct arm_short_io_pgtable *data,
+ unsigned int iova, phys_addr_t pa,
+ int prot, bool supersection)
+{
+ arm_short_iopte pgprot;
+ arm_short_iopte mask = supersection ?
+ ARM_SHORT_F_PGD_PA_SUPERSECTION_MSK :
+ ARM_SHORT_F_PGD_PA_SECTION_MSK;
+ arm_short_iopte *pgd = data->pgd;
+ int i;
+ unsigned int pgdnum = supersection ? 16 : 1;
+
+ if ((iova | pa) & (~mask)) {
+ pr_err("IOVA|PA Not Aligned(iova=0x%x pa=0x%pa type=%s)\n",
+ iova, &pa, supersection ? "supersection" : "section");
+ return -EINVAL;
+ }
+
+ pgprot = (arm_short_iopte)pa;
+
+ if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS)
+ pgprot |= ARM_SHORT_F_PGD_NS_BIT_SECTION;
+
+ pgprot |= __arm_short_pgd_port(prot, supersection);
+
+ pgd += ARM_SHORT_PGD_IDX(iova);
+
+ pr_debug("iova:0x%x,pgd:0x%p(0x%p+0x%x),value:0x%x-%s\n",
+ iova, pgd, data->pgd, ARM_SHORT_PGD_IDX(iova),
+ pgprot, supersection ? "supersection" : "section");
+
+ for (i = 0; i < pgdnum; i++) {
+ if (unlikely(*pgd)) {
+ pr_err("The To-Map pdg exists!(iova=0x%x pgd=0x%x i=%d)\n",
+ iova, pgd[i], i);
+ goto err_out;
+ }
+ pgd[i] = pgprot;
+ }
+ data->iop.cfg.tlb->flush_pgtable(pgd,
+ pgdnum * sizeof(arm_short_iopte),
+ data->iop.cookie);
+ return 0;
+
+ err_out:
+ for (i--; i >= 0; i--)
+ pgd[i] = 0;
+ return -EEXIST;
+}
+
+static int arm_short_map(struct io_pgtable_ops *ops, unsigned long iova,
+ phys_addr_t paddr, size_t size, int prot)
+{
+ struct arm_short_io_pgtable *data = io_pgtable_short_ops_to_data(ops);
+ const struct iommu_gather_ops *tlb = data->iop.cfg.tlb;
+ int ret;
+
+ if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
+ return -EINVAL;
+
+ if (size == SZ_4K) {/* most case */
+ ret = _arm_short_map_page(data, iova, paddr, prot, false);
+ } else if (size == SZ_64K) {
+ ret = _arm_short_map_page(data, iova, paddr, prot, true);
+ } else if (size == SZ_1M) {
+ ret = _arm_short_map_section(data, iova, paddr, prot, false);
+ } else if (size == SZ_16M) {
+ ret = _arm_short_map_section(data, iova, paddr, prot, true);
+ } else {
+ ret = -EINVAL;
+ }
+ tlb->tlb_add_flush(iova, size, true, data->iop.cookie);
+ return ret;
+}
+
+static struct io_pgtable *
+arm_short_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
+{
+ struct arm_short_io_pgtable *data;
+
+ if (cfg->ias != 32)
+ return NULL;
+
+ if (cfg->oas > ARM_SHORT_MAX_ADDR_BITS)
+ return NULL;
+
+ cfg->pgsize_bitmap &= SZ_4K | SZ_64K | SZ_1M | SZ_16M;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ data->pgd_size = SZ_16K;
+
+ data->pgd = alloc_pages_exact(data->pgd_size, GFP_KERNEL | __GFP_ZERO);
+ if (!data->pgd)
+ goto out_free_data;
+
+ cfg->tlb->flush_pgtable(data->pgd, data->pgd_size, cookie);
+
+ /* kmem for pte */
+ data->ptekmem = kmem_cache_create("short-descriptor-pte",
+ ARM_SHORT_BYTES_PER_PTE,
+ ARM_SHORT_BYTES_PER_PTE,
+ 0, NULL);
+
+ if (IS_ERR_OR_NULL(data->ptekmem)) {
+ pr_err("Failed to Create cached mem for PTE %ld\n",
+ PTR_ERR(data->ptekmem));
+ goto out_free_pte;
+ }
+
+ /* TTBRs */
+ cfg->arm_short_cfg.ttbr[0] = virt_to_phys(data->pgd);
+ cfg->arm_short_cfg.ttbr[1] = 0;
+
+ cfg->arm_short_cfg.tcr = 0;
+
+ data->iop.ops = (struct io_pgtable_ops) {
+ .map = arm_short_map,
+ .unmap = arm_short_unmap,
+ .iova_to_phys = arm_short_iova_to_phys,
+ };
+
+ return &data->iop;
+
+out_free_pte:
+ free_pages_exact(data->pgd, data->pgd_size);
+out_free_data:
+ kfree(data);
+ return NULL;
+}
+
+static void arm_short_free_pgtable(struct io_pgtable *iop)
+{
+ struct arm_short_io_pgtable *data = io_pgtable_short_to_data(iop);
+
+ kmem_cache_destroy(data->ptekmem);
+ free_pages_exact(data->pgd, data->pgd_size);
+ kfree(data);
+}
+
+struct io_pgtable_init_fns io_pgtable_arm_short_init_fns = {
+ .alloc = arm_short_alloc_pgtable,
+ .free = arm_short_free_pgtable,
+};
+
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
index 6436fe2..14a9b3a 100644
--- a/drivers/iommu/io-pgtable.c
+++ b/drivers/iommu/io-pgtable.c
@@ -28,6 +28,7 @@ extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns;
extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns;
extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns;
extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns;
+extern struct io_pgtable_init_fns io_pgtable_arm_short_init_fns;
static const struct io_pgtable_init_fns *
io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] =
@@ -38,6 +39,9 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] =
[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
#endif
+#ifdef CONFIG_IOMMU_IO_PGTABLE_SHORT
+ [ARM_SHORT_DESC] = &io_pgtable_arm_short_init_fns,
+#endif
};
struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt,
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
index 10e32f6..47efaab 100644
--- a/drivers/iommu/io-pgtable.h
+++ b/drivers/iommu/io-pgtable.h
@@ -9,6 +9,7 @@ enum io_pgtable_fmt {
ARM_32_LPAE_S2,
ARM_64_LPAE_S1,
ARM_64_LPAE_S2,
+ ARM_SHORT_DESC,
IO_PGTABLE_NUM_FMTS,
};
@@ -62,6 +63,11 @@ struct io_pgtable_cfg {
u64 vttbr;
u64 vtcr;
} arm_lpae_s2_cfg;
+
+ struct {
+ u64 ttbr[2];
+ u64 tcr;
+ } arm_short_cfg;
};
};
--
1.8.1.1.dirty
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] iommu: add ARM short descriptor page table allocator.
2015-04-28 7:41 [RFC PATCH] iommu: add ARM short descriptor page table allocator Yong Wu
@ 2015-05-05 16:18 ` Joerg Roedel
2015-05-05 17:05 ` Will Deacon
2015-05-12 9:15 ` Matthias Brugger
1 sibling, 1 reply; 8+ messages in thread
From: Joerg Roedel @ 2015-05-05 16:18 UTC (permalink / raw)
To: Yong Wu
Cc: Will Deacon, Matthias Brugger, Robin Murphy, Tomasz Figa,
Lucas Stach, linux-mediatek, Sascha Hauer, srv_heupstream,
linux-kernel, linux-arm-kernel, iommu
On Tue, Apr 28, 2015 at 03:41:21PM +0800, Yong Wu wrote:
> This patch is for ARM Short Descriptor Format.It has 2-levels
> pagetable and the allocator supports 4K/64K/1M/16M.
>
> Signed-off-by: Yong Wu <yong.wu@mediatek.com>
> ---
> drivers/iommu/Kconfig | 7 +
> drivers/iommu/Makefile | 1 +
> drivers/iommu/io-pgtable-arm-short.c | 489 +++++++++++++++++++++++++++++++++++
> drivers/iommu/io-pgtable.c | 4 +
> drivers/iommu/io-pgtable.h | 6 +
> 5 files changed, 507 insertions(+)
> create mode 100644 drivers/iommu/io-pgtable-arm-short.c
What is the context of this, where will it be used?
Joerg
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] iommu: add ARM short descriptor page table allocator.
2015-05-05 16:18 ` Joerg Roedel
@ 2015-05-05 17:05 ` Will Deacon
2015-05-11 10:13 ` Joerg Roedel
0 siblings, 1 reply; 8+ messages in thread
From: Will Deacon @ 2015-05-05 17:05 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yong Wu, Matthias Brugger, Robin Murphy, Tomasz Figa,
Lucas Stach, linux-mediatek, Sascha Hauer, srv_heupstream,
linux-kernel, linux-arm-kernel, iommu
On Tue, May 05, 2015 at 05:18:02PM +0100, Joerg Roedel wrote:
> On Tue, Apr 28, 2015 at 03:41:21PM +0800, Yong Wu wrote:
> > This patch is for ARM Short Descriptor Format.It has 2-levels
> > pagetable and the allocator supports 4K/64K/1M/16M.
> >
> > Signed-off-by: Yong Wu <yong.wu@mediatek.com>
> > ---
> > drivers/iommu/Kconfig | 7 +
> > drivers/iommu/Makefile | 1 +
> > drivers/iommu/io-pgtable-arm-short.c | 489 +++++++++++++++++++++++++++++++++++
> > drivers/iommu/io-pgtable.c | 4 +
> > drivers/iommu/io-pgtable.h | 6 +
> > 5 files changed, 507 insertions(+)
> > create mode 100644 drivers/iommu/io-pgtable-arm-short.c
>
> What is the context of this, where will it be used?
I think the MT8173 IOMMU [1] uses this format and, since it's part of
the ARM architecture, the ARM SMMU can make use of it too if the
implementation supports it.
Will
[1]
http://lists.infradead.org/pipermail/linux-mediatek/2015-March/000058.html
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] iommu: add ARM short descriptor page table allocator.
2015-05-05 17:05 ` Will Deacon
@ 2015-05-11 10:13 ` Joerg Roedel
2015-05-11 10:40 ` Will Deacon
0 siblings, 1 reply; 8+ messages in thread
From: Joerg Roedel @ 2015-05-11 10:13 UTC (permalink / raw)
To: Will Deacon
Cc: Yong Wu, Matthias Brugger, Robin Murphy, Tomasz Figa,
Lucas Stach, linux-mediatek, Sascha Hauer, srv_heupstream,
linux-kernel, linux-arm-kernel, iommu
On Tue, May 05, 2015 at 06:05:41PM +0100, Will Deacon wrote:
> I think the MT8173 IOMMU [1] uses this format and, since it's part of
> the ARM architecture, the ARM SMMU can make use of it too if the
> implementation supports it.
I think it makes more sense to merge this with a driver actually using
it. I guess a driver for the above mentioned IOMMU will be submitted
soon?
Joerg
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] iommu: add ARM short descriptor page table allocator.
2015-05-11 10:13 ` Joerg Roedel
@ 2015-05-11 10:40 ` Will Deacon
2015-05-12 5:30 ` Yong Wu
0 siblings, 1 reply; 8+ messages in thread
From: Will Deacon @ 2015-05-11 10:40 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yong Wu, Matthias Brugger, Robin Murphy, Tomasz Figa,
Lucas Stach, linux-mediatek, Sascha Hauer, srv_heupstream,
linux-kernel, linux-arm-kernel, iommu
On Mon, May 11, 2015 at 11:13:09AM +0100, Joerg Roedel wrote:
> On Tue, May 05, 2015 at 06:05:41PM +0100, Will Deacon wrote:
> > I think the MT8173 IOMMU [1] uses this format and, since it's part of
> > the ARM architecture, the ARM SMMU can make use of it too if the
> > implementation supports it.
>
> I think it makes more sense to merge this with a driver actually using
> it. I guess a driver for the above mentioned IOMMU will be submitted
> soon?
I completely agree; since there are already patches floating around for
that IOMMU, it's probably worth them being put into a single series.
Will
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] iommu: add ARM short descriptor page table allocator.
2015-05-11 10:40 ` Will Deacon
@ 2015-05-12 5:30 ` Yong Wu
0 siblings, 0 replies; 8+ messages in thread
From: Yong Wu @ 2015-05-12 5:30 UTC (permalink / raw)
To: Will Deacon, Joerg Roedel
Cc: Joerg Roedel, Matthias Brugger, Robin Murphy, Tomasz Figa,
Lucas Stach, linux-mediatek, Sascha Hauer, srv_heupstream,
linux-kernel, linux-arm-kernel, iommu
On Mon, 2015-05-11 at 11:40 +0100, Will Deacon wrote:
> On Mon, May 11, 2015 at 11:13:09AM +0100, Joerg Roedel wrote:
> > On Tue, May 05, 2015 at 06:05:41PM +0100, Will Deacon wrote:
> > > I think the MT8173 IOMMU [1] uses this format and, since it's part of
> > > the ARM architecture, the ARM SMMU can make use of it too if the
> > > implementation supports it.
> >
> > I think it makes more sense to merge this with a driver actually using
> > it. I guess a driver for the above mentioned IOMMU will be submitted
> > soon?
>
> I completely agree; since there are already patches floating around for
> that IOMMU, it's probably worth them being put into a single series.
>
> Will
Hi Joerg, Will,
Thanks. We will put this into the mtk-iommu patch series and
prepare to send in this week.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] iommu: add ARM short descriptor page table allocator.
2015-04-28 7:41 [RFC PATCH] iommu: add ARM short descriptor page table allocator Yong Wu
2015-05-05 16:18 ` Joerg Roedel
@ 2015-05-12 9:15 ` Matthias Brugger
2015-05-13 2:52 ` Yong Wu
1 sibling, 1 reply; 8+ messages in thread
From: Matthias Brugger @ 2015-05-12 9:15 UTC (permalink / raw)
To: Yong Wu
Cc: Will Deacon, Joerg Roedel, Robin Murphy, Tomasz Figa,
Lucas Stach, linux-mediatek, Sascha Hauer, srv_heupstream,
linux-kernel, linux-arm-kernel, iommu
2015-04-28 9:41 GMT+02:00 Yong Wu <yong.wu@mediatek.com>:
> This patch is for ARM Short Descriptor Format.It has 2-levels
> pagetable and the allocator supports 4K/64K/1M/16M.
>
> Signed-off-by: Yong Wu <yong.wu@mediatek.com>
> ---
> drivers/iommu/Kconfig | 7 +
> drivers/iommu/Makefile | 1 +
> drivers/iommu/io-pgtable-arm-short.c | 489 +++++++++++++++++++++++++++++++++++
> drivers/iommu/io-pgtable.c | 4 +
> drivers/iommu/io-pgtable.h | 6 +
> 5 files changed, 507 insertions(+)
> create mode 100644 drivers/iommu/io-pgtable-arm-short.c
>
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index baa0d97..8e50e73 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -38,6 +38,13 @@ config IOMMU_IO_PGTABLE_LPAE_SELFTEST
>
> If unsure, say N here.
>
> +config IOMMU_IO_PGTABLE_SHORT
> + bool "ARMv7/v8 Short Descriptor Format"
> + select IOMMU_IO_PGTABLE
> + help
> + Enable support for the ARM Short descriptor pagetable format.
> + It has 2-levels pagetable and The allocator supports 4K/64K/1M/16M.
> +
> endmenu
>
> config IOMMU_IOVA
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index 080ffab..815b3c8 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -3,6 +3,7 @@ obj-$(CONFIG_IOMMU_API) += iommu-traces.o
> obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
> obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o
> obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
> +obj-$(CONFIG_IOMMU_IO_PGTABLE_SHORT) += io-pgtable-arm-short.o
> obj-$(CONFIG_IOMMU_IOVA) += iova.o
> obj-$(CONFIG_OF_IOMMU) += of_iommu.o
> obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
> diff --git a/drivers/iommu/io-pgtable-arm-short.c b/drivers/iommu/io-pgtable-arm-short.c
> new file mode 100644
> index 0000000..8d723ec
> --- /dev/null
> +++ b/drivers/iommu/io-pgtable-arm-short.c
> @@ -0,0 +1,489 @@
> +/*
> + * Copyright (c) 2014-2015 MediaTek Inc.
> + * Author: Yong Wu <yong.wu@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +#define pr_fmt(fmt) "arm-short-desc io-pgtable: "fmt
> +
> +#include <linux/err.h>
> +#include <linux/mm.h>
> +#include <linux/iommu.h>
> +#include <linux/errno.h>
> +
> +#include "io-pgtable.h"
> +
> +typedef u32 arm_short_iopte;
> +
> +struct arm_short_io_pgtable {
> + struct io_pgtable iop;
> + struct kmem_cache *ptekmem;
> + size_t pgd_size;
> + void *pgd;
> +};
> +
> +#define io_pgtable_short_to_data(x) \
> + container_of((x), struct arm_short_io_pgtable, iop)
> +
> +#define io_pgtable_ops_to_pgtable(x) \
> + container_of((x), struct io_pgtable, ops)
> +
> +#define io_pgtable_short_ops_to_data(x) \
> + io_pgtable_short_to_data(io_pgtable_ops_to_pgtable(x))
> +
> +#define ARM_SHORT_MAX_ADDR_BITS 32
> +
> +#define ARM_SHORT_PGDIR_SHIFT 20
> +#define ARM_SHORT_PAGE_SHIFT 12
> +#define ARM_SHORT_PTRS_PER_PTE 256
> +#define ARM_SHORT_BYTES_PER_PTE 1024
> +
> +/* 1 level pagetable */
> +#define ARM_SHORT_F_PGD_TYPE_PAGE (0x1)
> +#define ARM_SHORT_F_PGD_TYPE_PAGE_MSK (0x3)
> +#define ARM_SHORT_F_PGD_TYPE_SECTION (0x2)
> +#define ARM_SHORT_F_PGD_TYPE_SUPERSECTION (0x2 | (1 << 18))
> +#define ARM_SHORT_F_PGD_TYPE_SECTION_MSK (0x3 | (1 << 18))
> +#define ARM_SHORT_F_PGD_TYPE_IS_PAGE(pgd) (((pgd) & 0x3) == 1)
> +#define ARM_SHORT_F_PGD_TYPE_IS_SECTION(pgd) \
> + (((pgd) & ARM_SHORT_F_PGD_TYPE_SECTION_MSK) \
> + == ARM_SHORT_F_PGD_TYPE_SECTION)
> +#define ARM_SHORT_F_PGD_TYPE_IS_SUPERSECTION(pgd) \
> + (((pgd) & ARM_SHORT_F_PGD_TYPE_SECTION_MSK) \
> + == ARM_SHORT_F_PGD_TYPE_SUPERSECTION)
> +
> +#define ARM_SHORT_F_PGD_B_BIT BIT(2)
> +#define ARM_SHORT_F_PGD_C_BIT BIT(3)
> +#define ARM_SHORT_F_PGD_IMPLE_BIT BIT(9)
> +#define ARM_SHORT_F_PGD_S_BIT BIT(16)
> +#define ARM_SHORT_F_PGD_NG_BIT BIT(17)
> +#define ARM_SHORT_F_PGD_NS_BIT_PAGE BIT(3)
> +#define ARM_SHORT_F_PGD_NS_BIT_SECTION BIT(19)
> +
> +#define ARM_SHORT_F_PGD_PA_PAGETABLE_MSK 0xfffffc00
> +#define ARM_SHORT_F_PGD_PA_SECTION_MSK 0xfff00000
> +#define ARM_SHORT_F_PGD_PA_SUPERSECTION_MSK 0xff000000
> +
> +/* 2 level pagetable */
> +#define ARM_SHORT_F_PTE_TYPE_GET(val) ((val) & 0x3)
> +#define ARM_SHORT_F_PTE_TYPE_LARGE BIT(0)
> +#define ARM_SHORT_F_PTE_TYPE_SMALL BIT(1)
> +#define ARM_SHORT_F_PTE_B_BIT BIT(2)
> +#define ARM_SHORT_F_PTE_C_BIT BIT(3)
> +#define ARM_SHORT_F_PTE_IMPLE_BIT BIT(9)
> +#define ARM_SHORT_F_PTE_S_BIT BIT(10)
> +#define ARM_SHORT_F_PTE_PA_LARGE_MSK 0xffff0000
> +#define ARM_SHORT_F_PTE_PA_SMALL_MSK 0xfffff000
> +
> +#define ARM_SHORT_PGD_IDX(a) ((a) >> ARM_SHORT_PGDIR_SHIFT)
> +#define ARM_SHORT_PTE_IDX(a) \
> + (((a) >> ARM_SHORT_PAGE_SHIFT) & 0xff)
> +#define ARM_SHORT_GET_PTE_VA(pgd) \
> + (phys_to_virt((unsigned long)pgd & ARM_SHORT_F_PGD_PA_PAGETABLE_MSK))
> +
> +static arm_short_iopte *
> +arm_short_get_pte_in_pgd(arm_short_iopte curpgd, unsigned int iova)
> +{
> + arm_short_iopte *pte;
> +
> + pte = ARM_SHORT_GET_PTE_VA(curpgd);
> + pte += ARM_SHORT_PTE_IDX(iova);
> + return pte;
> +}
> +
> +static arm_short_iopte *
> +arm_short_supersection_start(arm_short_iopte *pgd)
> +{
> + return (arm_short_iopte *)(round_down((unsigned long)pgd, (16 * 4)));
> +}
> +
> +static int _arm_short_check_free_pte(struct arm_short_io_pgtable *data,
> + arm_short_iopte *pgd)
> +{
> + arm_short_iopte *pte;
> + int i;
> +
> + pte = ARM_SHORT_GET_PTE_VA(*pgd);
> +
> + for (i = 0; i < ARM_SHORT_PTRS_PER_PTE; i++) {
> + if (pte[i] != 0)
> + return 1;
> + }
> +
> + /* Free PTE */
> + kmem_cache_free(data->ptekmem, pte);
> + *pgd = 0;
> +
> + return 0;
> +}
> +
> +static phys_addr_t arm_short_iova_to_phys(struct io_pgtable_ops *ops,
> + unsigned long iova)
> +{
> + struct arm_short_io_pgtable *data = io_pgtable_short_ops_to_data(ops);
> + arm_short_iopte *pte, *pgd = data->pgd;
> + phys_addr_t pa = 0;
> +
> + pgd += ARM_SHORT_PGD_IDX(iova);
> +
> + if (ARM_SHORT_F_PGD_TYPE_IS_PAGE(*pgd)) {
> + pte = arm_short_get_pte_in_pgd(*pgd, iova);
> +
> + if (ARM_SHORT_F_PTE_TYPE_GET(*pte)
> + == ARM_SHORT_F_PTE_TYPE_LARGE) {
> + pa = (*pte) & ARM_SHORT_F_PTE_PA_LARGE_MSK;
> + pa |= iova & (~ARM_SHORT_F_PTE_PA_LARGE_MSK);
> + } else if (ARM_SHORT_F_PTE_TYPE_GET(*pte)
> + == ARM_SHORT_F_PTE_TYPE_SMALL) {
Would make it easier to read:
u8 pte_type;
[...]
pte_type = ARM_SHORT_F_PTE_TYPE_GET(*pte)
if (pte_type == ARM_SHORT_F_PTE_TYPE_LARGE) {
[...]
} else if (pte_type == ARM_SHORT_F_PTE_TYPE_SMALL) {
[...]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] iommu: add ARM short descriptor page table allocator.
2015-05-12 9:15 ` Matthias Brugger
@ 2015-05-13 2:52 ` Yong Wu
0 siblings, 0 replies; 8+ messages in thread
From: Yong Wu @ 2015-05-13 2:52 UTC (permalink / raw)
To: Matthias Brugger
Cc: Will Deacon, Joerg Roedel, Robin Murphy, Tomasz Figa,
Lucas Stach, linux-mediatek, Sascha Hauer, srv_heupstream,
linux-kernel, linux-arm-kernel, iommu
On Tue, 2015-05-12 at 11:15 +0200, Matthias Brugger wrote:
> 2015-04-28 9:41 GMT+02:00 Yong Wu <yong.wu@mediatek.com>:
> > This patch is for ARM Short Descriptor Format.It has 2-levels
> > pagetable and the allocator supports 4K/64K/1M/16M.
> >
> > Signed-off-by: Yong Wu <yong.wu@mediatek.com>
[snip]
> > +static phys_addr_t arm_short_iova_to_phys(struct io_pgtable_ops *ops,
> > + unsigned long iova)
> > +{
> > + struct arm_short_io_pgtable *data = io_pgtable_short_ops_to_data(ops);
> > + arm_short_iopte *pte, *pgd = data->pgd;
> > + phys_addr_t pa = 0;
> > +
> > + pgd += ARM_SHORT_PGD_IDX(iova);
> > +
> > + if (ARM_SHORT_F_PGD_TYPE_IS_PAGE(*pgd)) {
> > + pte = arm_short_get_pte_in_pgd(*pgd, iova);
> > +
> > + if (ARM_SHORT_F_PTE_TYPE_GET(*pte)
> > + == ARM_SHORT_F_PTE_TYPE_LARGE) {
> > + pa = (*pte) & ARM_SHORT_F_PTE_PA_LARGE_MSK;
> > + pa |= iova & (~ARM_SHORT_F_PTE_PA_LARGE_MSK);
> > + } else if (ARM_SHORT_F_PTE_TYPE_GET(*pte)
> > + == ARM_SHORT_F_PTE_TYPE_SMALL) {
>
> Would make it easier to read:
> u8 pte_type;
>
> [...]
>
> pte_type = ARM_SHORT_F_PTE_TYPE_GET(*pte)
> if (pte_type == ARM_SHORT_F_PTE_TYPE_LARGE) {
> [...]
> } else if (pte_type == ARM_SHORT_F_PTE_TYPE_SMALL) {
> [...]
Hi Matthias,
Thanks for your review.I will fix this in next version.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2015-05-13 2:53 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-04-28 7:41 [RFC PATCH] iommu: add ARM short descriptor page table allocator Yong Wu
2015-05-05 16:18 ` Joerg Roedel
2015-05-05 17:05 ` Will Deacon
2015-05-11 10:13 ` Joerg Roedel
2015-05-11 10:40 ` Will Deacon
2015-05-12 5:30 ` Yong Wu
2015-05-12 9:15 ` Matthias Brugger
2015-05-13 2:52 ` Yong Wu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).