[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v03 05/10] arm: omap: introduce iommu translation for IPU remoteproc



The following patch introduced platform specific MMU data
definitions and pagetable translation function for OMAP5 IPU
remoteproc. This MMU is a bit specific - it typically performs
one level translation and map a big chunks of memory. 16 Mb
supersections and 1 Mb sections are mapped instead of 4 Kb pages.
Introduced algorithm performs internal remapping of big sections
to small 4 Kb pages.

Signed-off-by: Andrii Tseglytskyi <andrii.tseglytskyi@xxxxxxxxxxxxxxx>
---
 xen/arch/arm/remoteproc/Makefile           |   1 +
 xen/arch/arm/remoteproc/omap_iommu.c       | 325 +++++++++++++++++++++++++++++
 xen/arch/arm/remoteproc/remoteproc_iommu.c |   1 +
 xen/include/asm-arm/remoteproc_iommu.h     |   2 +
 4 files changed, 329 insertions(+)
 create mode 100644 xen/arch/arm/remoteproc/omap_iommu.c

diff --git a/xen/arch/arm/remoteproc/Makefile b/xen/arch/arm/remoteproc/Makefile
index 0b0ee0e..0564c1a 100644
--- a/xen/arch/arm/remoteproc/Makefile
+++ b/xen/arch/arm/remoteproc/Makefile
@@ -1 +1,2 @@
 obj-y += remoteproc_iommu.o
+obj-y += omap_iommu.o
diff --git a/xen/arch/arm/remoteproc/omap_iommu.c 
b/xen/arch/arm/remoteproc/omap_iommu.c
new file mode 100644
index 0000000..8ed6d0b
--- /dev/null
+++ b/xen/arch/arm/remoteproc/omap_iommu.c
@@ -0,0 +1,325 @@
+/*
+ * xen/arch/arm/platforms/omap_iommu.c
+ *
+ * Andrii Tseglytskyi <andrii.tseglytskyi@xxxxxxxxxxxxxxx>
+ * Copyright (c) 2014 GlobalLogic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#include <xen/lib.h>
+#include <xen/errno.h>
+#include <xen/stdbool.h>
+#include <xen/mm.h>
+#include <xen/domain_page.h>
+#include <xen/sched.h>
+
+#include <asm/p2m.h>
+#include <asm-arm/remoteproc_iommu.h>
+
+/*
+ * "L2 table" address mask and size definitions.
+ */
+
+/* register where address of pagetable is stored */
+#define MMU_IPU_TTB_OFFSET          0x4c
+
+/* 1st level translation */
+#define MMU_OMAP_PGD_SHIFT          20
+#define MMU_OMAP_SUPER_SHIFT        24 /* "supersection" - 16 Mb */
+#define MMU_OMAP_SECTION_SHIFT      20 /* "section"  - 1 Mb */
+#define MMU_OMAP_SECOND_LEVEL_SHIFT 10
+
+/* 2nd level translation */
+#define MMU_OMAP_PTE_SMALL_SHIFT    12 /* "small page" - 4Kb */
+#define MMU_OMAP_PTE_LARGE_SHIFT    16 /* "large page" - 64 Kb */
+
+/*
+ * some descriptor attributes.
+ */
+#define IPU_PGD_TABLE       (1 << 0)
+#define IPU_PGD_SECTION     (2 << 0)
+#define IPU_PGD_SUPER       (1 << 18 | 2 << 0)
+
+#define ipu_pgd_is_table(x)     (((x) & 3) == IPU_PGD_TABLE)
+#define ipu_pgd_is_section(x)   (((x) & (1 << 18 | 3)) == IPU_PGD_SECTION)
+#define ipu_pgd_is_super(x)     (((x) & (1 << 18 | 3)) == IPU_PGD_SUPER)
+
+#define IPU_PTE_SMALL       (2 << 0)
+#define IPU_PTE_LARGE       (1 << 0)
+
+#define        OMAP_IPU_MMU_MEM_BASE   0x55082000
+
+static int mmu_omap_copy_pagetable(struct mmu_info *mmu, struct mmu_pagetable 
*pgt);
+
+static paddr_t mmu_ipu_translate_pagetable(struct mmu_info *mmu, struct 
mmu_pagetable *pgt);
+
+static u32 ipu_trap_offsets[] = {
+    MMU_IPU_TTB_OFFSET,
+};
+
+static const struct pagetable_data pagetable_ipu_data = {
+    .pgd_shift          = MMU_OMAP_PGD_SHIFT,
+    .super_shift        = MMU_OMAP_SUPER_SHIFT,
+    .section_shift      = MMU_OMAP_SECTION_SHIFT,
+    .pte_shift          = MMU_OMAP_PTE_SMALL_SHIFT,
+    .pte_large_shift    = MMU_OMAP_PTE_LARGE_SHIFT,
+};
+
+struct mmu_info omap_ipu_mmu = {
+    .name           = "IPU_L2_MMU",
+    .pg_data        = &pagetable_ipu_data,
+    .trap_offsets   = ipu_trap_offsets,
+    .mem_start      = OMAP_IPU_MMU_MEM_BASE,
+    .mem_size       = 0x1000,
+    .num_traps          = ARRAY_SIZE(ipu_trap_offsets),
+    .copy_pagetable_pfunc      = mmu_omap_copy_pagetable,
+    .translate_pfunc   = mmu_ipu_translate_pagetable,
+};
+
+static bool translate_supersections_to_pages = true;
+static bool translate_sections_to_pages = true;
+
+static int mmu_omap_copy_pagetable(struct mmu_info *mmu, struct mmu_pagetable 
*pgt)
+{
+    void *pagetable = NULL;
+    paddr_t maddr;
+    u32 i;
+
+    ASSERT(mmu);
+    ASSERT(pgt);
+
+    if ( !pgt->paddr )
+        return -EINVAL;
+
+    /* pagetable size can be more than one page */
+    for ( i = 0; i < MMU_PGD_TABLE_SIZE(mmu) / PAGE_SIZE; i++ )
+    {
+        /* lookup address where remoteproc pagetable is stored by kernel */
+        maddr = p2m_lookup(current->domain, pgt->paddr + i * PAGE_SIZE, NULL);
+        if ( INVALID_PADDR == maddr )
+        {
+            pr_mmu(mmu, "failed to translate 0x%"PRIpaddr" to maddr", 
pgt->paddr + i * PAGE_SIZE);
+            return -EINVAL;
+        }
+
+        pagetable = map_domain_page(maddr>>PAGE_SHIFT);
+        if ( !pagetable )
+        {
+            pr_mmu(mmu, "failed to map pagetable");
+            return -EINVAL;
+        }
+
+        /* copy pagetable to hypervisor memory */
+        clean_and_invalidate_xen_dcache_va_range(pagetable, PAGE_SIZE);
+        memcpy((u32*)((u32)pgt->kern_pagetable + i * PAGE_SIZE), pagetable, 
PAGE_SIZE);
+
+        unmap_domain_page(pagetable);
+    }
+
+    return 0;
+}
+
+static paddr_t mmu_pte_table_alloc(struct mmu_info *mmu, paddr_t pgd, u32 
sect_num,
+                               struct mmu_pagetable *pgt, paddr_t hyp_addr)
+{
+    u32 *pte = NULL;
+    u32 i;
+
+    /* allocate pte table once */
+    if ( 0 == hyp_addr )
+    {
+        pte = xzalloc_bytes(PAGE_SIZE);
+        if ( !pte )
+        {
+            pr_mmu(mmu, "failed to alloc 2nd level table");
+            return INVALID_PADDR;
+        }
+    }
+    else
+    {
+        pte = __va(hyp_addr & MMU_SECTION_MASK(mmu->pg_data->pte_shift));
+    }
+
+    ASSERT(256 == MMU_PTRS_PER_PTE(mmu));
+
+    for ( i = 0; i < MMU_PTRS_PER_PTE(mmu); i++ )
+    {
+        paddr_t paddr, maddr;
+        int res;
+
+        paddr = pgd + (i * PAGE_SIZE);
+        maddr = p2m_lookup(current->domain, paddr, NULL);
+
+        if ( INVALID_PADDR == maddr )
+        {
+            pr_mmu(mmu, "failed to lookup paddr 0x%"PRIpaddr"", paddr);
+            return INVALID_PADDR;
+        }
+
+        if ( !guest_physmap_pinned_range(current->domain, maddr >> PAGE_SHIFT, 
0) )
+        {
+            res = guest_physmap_pin_range(current->domain, maddr >> 
PAGE_SHIFT, 0);
+            if ( res )
+            {
+                pr_mmu(mmu, "can't pin page pfn 0x%"PRIpaddr" mfn 
0x%"PRIpaddr" res %d",
+                       paddr, maddr, res);
+                return INVALID_PADDR;
+            }
+        }
+
+        pte[i] = maddr | IPU_PTE_SMALL;
+        pgt->page_counter++;
+    }
+
+    clean_and_invalidate_xen_dcache_va_range(pte, PAGE_SIZE);
+    return __pa(pte) | IPU_PGD_TABLE;
+}
+
+static paddr_t mmu_ipu_translate_pagetable(struct mmu_info *mmu, struct 
mmu_pagetable *pgt)
+{
+    /* IPU pagetable consists of set of 32 bit pointers */
+    u32 *kern_pgt, *hyp_pgt;
+    const struct pagetable_data *data;
+    u32 i;
+
+    ASSERT(mmu);
+    ASSERT(pgt);
+
+    data = mmu->pg_data;
+    kern_pgt = pgt->kern_pagetable;
+    hyp_pgt = pgt->hyp_pagetable;
+    pgt->page_counter = 0;
+
+    ASSERT(4096 == MMU_PTRS_PER_PGD(mmu));
+
+    /* 1-st level translation */
+    for ( i = 0; i < MMU_PTRS_PER_PGD(mmu); i++ )
+    {
+        paddr_t pd_maddr, pd_paddr, pd_flags, pgd_tmp;
+        paddr_t pgd = kern_pgt[i];
+        u32 pd_mask = 0;
+        int res;
+
+        if ( !pgd )
+        {
+            /* handle the case when second level translation table
+             * was removed from kernel */
+            if ( unlikely(hyp_pgt[i]) )
+            {
+                guest_physmap_unpin_range(current->domain,
+                                (hyp_pgt[i] & 
MMU_SECTION_MASK(MMU_OMAP_SECOND_LEVEL_SHIFT)) >> PAGE_SHIFT, 0);
+                xfree(__va(hyp_pgt[i] & 
MMU_SECTION_MASK(MMU_OMAP_SECOND_LEVEL_SHIFT)));
+                hyp_pgt[i] = 0;
+            }
+
+            continue;
+        }
+
+        /* first level pointers have different formats, depending on their 
type */
+        if ( ipu_pgd_is_super(pgd) )
+            pd_mask = MMU_SECTION_MASK(MMU_OMAP_SUPER_SHIFT);
+        else if ( ipu_pgd_is_section(pgd) )
+            pd_mask = MMU_SECTION_MASK(MMU_OMAP_SECTION_SHIFT);
+        else if ( ipu_pgd_is_table(pgd) )
+            pd_mask = MMU_SECTION_MASK(MMU_OMAP_SECOND_LEVEL_SHIFT);
+
+        pd_paddr = pgd & pd_mask;
+        pd_flags = pgd & ~pd_mask;
+        pd_maddr = p2m_lookup(current->domain, pd_paddr, NULL);
+
+        if ( INVALID_PADDR == pd_maddr )
+        {
+            pr_mmu(mmu, "failed to lookup paddr 0x%"PRIpaddr"", pd_paddr);
+            return INVALID_PADDR;
+        }
+
+        if ( !guest_physmap_pinned_range(current->domain, pd_maddr >> 
PAGE_SHIFT, 0) )
+        {
+            res = guest_physmap_pin_range(current->domain, pd_maddr >> 
PAGE_SHIFT, 0);
+            if ( res )
+            {
+                pr_mmu(mmu, "can't pin page pfn 0x%"PRIpaddr" mfn 
0x%"PRIpaddr" res %d", pd_paddr, pd_maddr, res);
+                return INVALID_PADDR;
+            }
+        }
+
+        /* "supersection" 16 Mb */
+        if ( ipu_pgd_is_super(pgd) )
+        {
+            /* mapping of 16 Mb chunk is fragmented to 4 Kb pages */
+            if( likely(translate_supersections_to_pages) )
+            {
+                u32 j;
+
+                ASSERT(16 == MMU_SECTION_PER_SUPER(mmu));
+                ASSERT(1048576 == MMU_SECTION_SIZE(data->section_shift));
+
+                /* 16 Mb supersection is divided to 16 sections of 1 MB size */
+                for ( j = 0 ; j < MMU_SECTION_PER_SUPER(mmu); j++ )
+                {
+                    pgd_tmp = (pgd & ~IPU_PGD_SUPER) + (j * 
MMU_SECTION_SIZE(data->section_shift));
+                    hyp_pgt[i + j] = mmu_pte_table_alloc(mmu, pgd_tmp, i, pgt, 
hyp_pgt[i + j]);
+                }
+
+                /* move counter after supersection is translated */
+                i += (j - 1);
+            }
+            else
+            {
+                hyp_pgt[i] = pd_maddr | pd_flags;
+            }
+
+        /* "section" 1Mb */
+        }
+        else if ( ipu_pgd_is_section(pgd) )
+        {
+            if ( likely(translate_sections_to_pages) )
+            {
+                pgd_tmp = (pgd & ~IPU_PGD_SECTION);
+                hyp_pgt[i] = mmu_pte_table_alloc(mmu, pgd_tmp, i, pgt, 
hyp_pgt[i]);
+            }
+            else
+            {
+                hyp_pgt[i] = pd_maddr | pd_flags;
+            }
+
+        /* "table" */
+        }
+        else if ( unlikely(ipu_pgd_is_table(pgd)) )
+        {
+            ASSERT(256 == MMU_PTRS_PER_PTE(mmu));
+
+            hyp_pgt[i] = remoteproc_iommu_translate_second_level(mmu, pgt, 
pd_maddr, hyp_pgt[i]);
+            hyp_pgt[i] |= pd_flags;
+
+        /* error */
+        }
+        else
+        {
+            pr_mmu(mmu, "unknown entry %u: 0x%"PRIpaddr"", i, pgd);
+            return INVALID_PADDR;
+        }
+    }
+
+    /* force omap IOMMU to use new pagetable */
+    clean_and_invalidate_xen_dcache_va_range(hyp_pgt, MMU_PGD_TABLE_SIZE(mmu));
+    return __pa(hyp_pgt);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/arm/remoteproc/remoteproc_iommu.c 
b/xen/arch/arm/remoteproc/remoteproc_iommu.c
index e73711a..a2cae25 100644
--- a/xen/arch/arm/remoteproc/remoteproc_iommu.c
+++ b/xen/arch/arm/remoteproc/remoteproc_iommu.c
@@ -32,6 +32,7 @@
 #include <asm/remoteproc_iommu.h>
 
 static struct mmu_info *mmu_list[] = {
+    &omap_ipu_mmu,
 };
 
 #define mmu_for_each(pfunc, data)                       \
diff --git a/xen/include/asm-arm/remoteproc_iommu.h 
b/xen/include/asm-arm/remoteproc_iommu.h
index 6fa78ee..e581fc3 100644
--- a/xen/include/asm-arm/remoteproc_iommu.h
+++ b/xen/include/asm-arm/remoteproc_iommu.h
@@ -79,4 +79,6 @@ paddr_t remoteproc_iommu_translate_second_level(struct 
mmu_info *mmu,
                                                  struct mmu_pagetable *pgt,
                                                  paddr_t maddr, paddr_t 
hyp_addr);
 
+extern struct mmu_info omap_ipu_mmu;
+
 #endif /* _REMOTEPROC_IOMMU_H_ */
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.