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

[Xen-devel] [RFC v01 1/3] arm: omap: introduce iommu module



omap IOMMU module is designed to handle access to external
omap MMUs, connected to the L3 bus.

Change-Id: I96bbf2738e9dd2e21662e0986ca15c60183e669e
Signed-off-by: Andrii Tseglytskyi <andrii.tseglytskyi@xxxxxxxxxxxxxxx>
---
 xen/arch/arm/Makefile     |    1 +
 xen/arch/arm/io.c         |    1 +
 xen/arch/arm/io.h         |    1 +
 xen/arch/arm/omap_iommu.c |  415 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 418 insertions(+)
 create mode 100644 xen/arch/arm/omap_iommu.c

diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 003ac84..cb0b385 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -14,6 +14,7 @@ obj-y += io.o
 obj-y += irq.o
 obj-y += kernel.o
 obj-y += mm.o
+obj-y += omap_iommu.o
 obj-y += p2m.o
 obj-y += percpu.o
 obj-y += guestcopy.o
diff --git a/xen/arch/arm/io.c b/xen/arch/arm/io.c
index a6db00b..3281b67 100644
--- a/xen/arch/arm/io.c
+++ b/xen/arch/arm/io.c
@@ -26,6 +26,7 @@ static const struct mmio_handler *const mmio_handlers[] =
 {
     &vgic_distr_mmio_handler,
     &vuart_mmio_handler,
+       &mmu_mmio_handler,
 };
 #define MMIO_HANDLER_NR ARRAY_SIZE(mmio_handlers)
 
diff --git a/xen/arch/arm/io.h b/xen/arch/arm/io.h
index 8d252c0..acb5dff 100644
--- a/xen/arch/arm/io.h
+++ b/xen/arch/arm/io.h
@@ -42,6 +42,7 @@ struct mmio_handler {
 
 extern const struct mmio_handler vgic_distr_mmio_handler;
 extern const struct mmio_handler vuart_mmio_handler;
+extern const struct mmio_handler mmu_mmio_handler;
 
 extern int handle_mmio(mmio_info_t *info);
 
diff --git a/xen/arch/arm/omap_iommu.c b/xen/arch/arm/omap_iommu.c
new file mode 100644
index 0000000..4dab30f
--- /dev/null
+++ b/xen/arch/arm/omap_iommu.c
@@ -0,0 +1,415 @@
+/*
+ * xen/arch/arm/omap_iommu.c
+ *
+ * Andrii Tseglytskyi <andrii.tseglytskyi@xxxxxxxxxxxxxxx>
+ * Copyright (c) 2013 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/config.h>
+#include <xen/lib.h>
+#include <xen/errno.h>
+#include <xen/mm.h>
+#include <xen/vmap.h>
+#include <xen/init.h>
+#include <xen/sched.h>
+#include <xen/stdbool.h>
+#include <asm/system.h>
+#include <asm/current.h>
+#include <asm/io.h>
+#include <asm/p2m.h>
+
+#include "io.h"
+
+/* register where address of page table is stored */
+#define MMU_TTB                        0x4c
+
+/*
+ * "L2 table" address mask and size definitions.
+ */
+
+/* 1st level translation */
+#define IOPGD_SHIFT            20
+#define IOPGD_SIZE             (1UL << IOPGD_SHIFT)
+#define IOPGD_MASK             (~(IOPGD_SIZE - 1))
+
+/* "supersection" - 16 Mb */
+#define IOSUPER_SHIFT          24
+#define IOSUPER_SIZE           (1UL << IOSUPER_SHIFT)
+#define IOSUPER_MASK           (~(IOSUPER_SIZE - 1))
+
+/* "section"  - 1 Mb */
+#define IOSECTION_SHIFT                20
+#define IOSECTION_SIZE         (1UL << IOSECTION_SHIFT)
+#define IOSECTION_MASK         (~(IOSECTION_SIZE - 1))
+
+/* 4096 first level descriptors for "supersection" and "section" */
+#define PTRS_PER_IOPGD         (1UL << (32 - IOPGD_SHIFT))
+#define IOPGD_TABLE_SIZE       (PTRS_PER_IOPGD * sizeof(u32))
+
+/* 2nd level translation */
+
+/* "small page" - 4Kb */
+#define IOPTE_SMALL_SHIFT              12
+#define IOPTE_SMALL_SIZE               (1UL << IOPTE_SMALL_SHIFT)
+#define IOPTE_SMALL_MASK               (~(IOPTE_SMALL_SIZE - 1))
+
+/* "large page" - 64 Kb */
+#define IOPTE_LARGE_SHIFT              16
+#define IOPTE_LARGE_SIZE               (1UL << IOPTE_LARGE_SHIFT)
+#define IOPTE_LARGE_MASK               (~(IOPTE_LARGE_SIZE - 1))
+
+/* 256 second level descriptors for "small" and "large" pages */
+#define PTRS_PER_IOPTE         (1UL << (IOPGD_SHIFT - IOPTE_SMALL_SHIFT))
+#define IOPTE_TABLE_SIZE       (PTRS_PER_IOPTE * sizeof(u32))
+
+/*
+ * some descriptor attributes.
+ */
+#define IOPGD_TABLE            (1 << 0)
+#define IOPGD_SECTION  (2 << 0)
+#define IOPGD_SUPER            (1 << 18 | 2 << 0)
+
+#define iopgd_is_table(x)      (((x) & 3) == IOPGD_TABLE)
+#define iopgd_is_section(x)    (((x) & (1 << 18 | 3)) == IOPGD_SECTION)
+#define iopgd_is_super(x)      (((x) & (1 << 18 | 3)) == IOPGD_SUPER)
+
+#define IOPTE_SMALL            (2 << 0)
+#define IOPTE_LARGE            (1 << 0)
+
+#define iopte_is_small(x)      (((x) & 2) == IOPTE_SMALL)
+#define iopte_is_large(x)      (((x) & 3) == IOPTE_LARGE)
+#define iopte_offset(x)                ((x) & IOPTE_SMALL_MASK)
+
+struct mmu_info {
+       const char                      *name;
+       paddr_t                         mem_start;
+       u32                                     mem_size;
+       u32                                     *pagetable;
+       void __iomem            *mem_map;
+};
+
+static struct mmu_info omap_ipu_mmu = {
+       .name           = "IPU_L2_MMU",
+       .mem_start      = 0x55082000,
+       .mem_size       = 0x1000,
+       .pagetable      = NULL,
+};
+
+static struct mmu_info omap_dsp_mmu = {
+       .name           = "DSP_L2_MMU",
+       .mem_start      = 0x4a066000,
+       .mem_size       = 0x1000,
+       .pagetable      = NULL,
+};
+
+static struct mmu_info *mmu_list[] = {
+       &omap_ipu_mmu,
+       &omap_dsp_mmu,
+};
+
+#define mmu_for_each(pfunc, data)                                              
\
+({                                                                             
                                \
+       u32 __i;                                                                
                        \
+       int __res = 0;                                                          
                \
+                                                                               
                                \
+       for (__i = 0; __i < ARRAY_SIZE(mmu_list); __i++) {      \
+               __res |= pfunc(mmu_list[__i], data);                    \
+       }                                                                       
                                \
+       __res;                                                                  
                        \
+})
+
+static int mmu_check_mem_range(struct mmu_info *mmu, paddr_t addr)
+{
+       if ((addr >= mmu->mem_start) && (addr < (mmu->mem_start + 
mmu->mem_size)))
+               return 1;
+
+       return 0;
+}
+
+static inline struct mmu_info *mmu_lookup(u32 addr)
+{
+       u32 i;
+
+       for (i = 0; i < ARRAY_SIZE(mmu_list); i++) {
+               if (mmu_check_mem_range(mmu_list[i], addr))
+                       return mmu_list[i];
+       }
+
+       return NULL;
+}
+
+static inline u32 mmu_virt_to_phys(u32 reg, u32 va, u32 mask)
+{
+       return (reg & mask) | (va & (~mask));
+}
+
+static inline u32 mmu_phys_to_virt(u32 reg, u32 pa, u32 mask)
+{
+       return (reg & ~mask) | pa;
+}
+
+static int mmu_mmio_check(struct vcpu *v, paddr_t addr)
+{
+       return mmu_for_each(mmu_check_mem_range, addr);
+}
+
+static int mmu_copy_pagetable(struct mmu_info *mmu)
+{
+       void __iomem *pagetable = NULL;
+       u32 pgaddr;
+
+       ASSERT(mmu);
+
+       /* read address where kernel MMU pagetable is stored */
+       pgaddr = readl(mmu->mem_map + MMU_TTB);
+       pagetable = ioremap(pgaddr, IOPGD_TABLE_SIZE);
+       if (!pagetable) {
+               printk("%s: %s failed to map pagetable\n",
+                          __func__, mmu->name);
+               return -EINVAL;
+       }
+
+       /*
+        * pagetable can be changed since last time
+        * we accessed it therefore we need to copy it each time
+        */
+       memcpy(mmu->pagetable, pagetable, IOPGD_TABLE_SIZE);
+
+       iounmap(pagetable);
+
+       return 0;
+}
+
+#define mmu_dump_pdentry(da, iopgd, paddr, maddr, vaddr, mask)                 
                                                \
+{                                                                              
                                                                                
                                \
+       const char *sect_type = (iopgd_is_table(iopgd) || (mask == 
IOPTE_SMALL_MASK) ||                         \
+                                                       (mask == 
IOPTE_LARGE_MASK)) ? "table"                                                    
       \
+                                                       : iopgd_is_super(iopgd) 
? "supersection"                                                        \
+                                                       : 
iopgd_is_section(iopgd) ? "section"                                             
              \
+                                                       : "Unknown section";    
                                                                                
        \
+       printk("[iopgd] %s da 0x%08x iopgd 0x%08x paddr 0x%08x maddr 0x%pS 
vaddr 0x%08x mask 0x%08x\n",\
+                  sect_type, da, iopgd, paddr, _p(maddr), vaddr, mask);        
                                                        \
+}
+
+static u32 mmu_translate_pgentry(struct domain *dom, u32 iopgd, u32 da, u32 
mask)
+{
+       u32 vaddr, paddr;
+       paddr_t maddr;
+
+       paddr = mmu_virt_to_phys(iopgd, da, mask);
+       maddr = p2m_lookup(dom, paddr);
+       vaddr = mmu_phys_to_virt(iopgd, maddr, mask);
+
+       return vaddr;
+}
+
+/*
+ * on boot table is empty
+ */
+static int mmu_translate_pagetable(struct domain *dom, struct mmu_info *mmu)
+{
+       u32 i;
+       int res;
+       bool table_updated = false;
+
+       ASSERT(dom);
+       ASSERT(mmu);
+
+       /* copy pagetable from  domain to xen */
+       res = mmu_copy_pagetable(mmu);
+       if (res) {
+               printk("%s: %s failed to map pagetable memory\n",
+                          __func__, mmu->name);
+               return res;
+       }
+
+       /* 1-st level translation */
+       for (i = 0; i < PTRS_PER_IOPGD; i++) {
+               u32 da;
+               u32 iopgd = mmu->pagetable[i];
+
+               if (!iopgd)
+                       continue;
+
+               table_updated = true;
+
+               /* "supersection" 16 Mb */
+               if (iopgd_is_super(iopgd)) {
+                       da = i << IOSECTION_SHIFT;
+                       mmu->pagetable[i] = mmu_translate_pgentry(dom, iopgd, 
da, IOSUPER_MASK);
+
+               /* "section" 1Mb */
+               } else if (iopgd_is_section(iopgd)) {
+                       da = i << IOSECTION_SHIFT;
+                       mmu->pagetable[i] = mmu_translate_pgentry(dom, iopgd, 
da, IOSECTION_MASK);
+
+               /* "table" */
+               } else if (iopgd_is_table(iopgd)) {
+                       u32 j, mask;
+                       u32 iopte = iopte_offset(iopgd);
+
+                       /* 2-nd level translation */
+                       for (j = 0; j < PTRS_PER_IOPTE; j++, iopte += 
IOPTE_SMALL_SIZE) {
+
+                               /* "small table" 4Kb */
+                               if (iopte_is_small(iopgd)) {
+                                       da = (i << IOSECTION_SHIFT) + (j << 
IOPTE_SMALL_SHIFT);
+                                       mask = IOPTE_SMALL_MASK;
+
+                               /* "large table" 64Kb */
+                               } else if (iopte_is_large(iopgd)) {
+                                       da = (i << IOSECTION_SHIFT) + (j << 
IOPTE_LARGE_SHIFT);
+                                       mask = IOPTE_LARGE_MASK;
+
+                               /* error */
+                               } else {
+                                       printk("%s Unknown table type 
0x%08x\n", mmu->name, iopte);
+                                       return -EINVAL;
+                               }
+
+                               /* translate 2-nd level entry */
+                               mmu->pagetable[i] = mmu_translate_pgentry(dom, 
iopte, da, mask);
+                       }
+
+                       continue;
+
+               /* error */
+               } else {
+                       printk("%s Unknown entry 0x%08x\n", mmu->name, iopgd);
+                       return -EINVAL;
+               }
+       }
+
+       /* force omap IOMMU to use new pagetable */
+       if (table_updated) {
+               paddr_t maddr;
+               flush_xen_dcache_va_range(mmu->pagetable, IOPGD_TABLE_SIZE);
+               maddr = __pa(mmu->pagetable);
+               writel(maddr, mmu->mem_map + MMU_TTB);
+               printk("%s update pagetable, maddr 0x%pS\n", mmu->name, 
_p(maddr));
+       }
+
+       return 0;
+}
+
+static int mmu_trap_write_access(struct domain *dom,
+                                                                struct 
mmu_info *mmu, mmio_info_t *info)
+{
+       struct cpu_user_regs *regs = guest_cpu_user_regs();
+       register_t *r = select_user_reg(regs, info->dabt.reg);
+       int res = 0;
+
+       switch (info->gpa - mmu->mem_start) {
+               case MMU_TTB:
+                       printk("%s MMU_TTB write access 0x%pS <= 0x%08x\n",
+                                  mmu->name, _p(info->gpa), *r);
+                       res = mmu_translate_pagetable(dom, mmu);
+                       break;
+               default:
+                       break;
+       }
+
+       return res;
+}
+
+static int mmu_mmio_read(struct vcpu *v, mmio_info_t *info)
+{
+       struct mmu_info *mmu = NULL;
+    struct cpu_user_regs *regs = guest_cpu_user_regs();
+    register_t *r = select_user_reg(regs, info->dabt.reg);
+
+       mmu = mmu_lookup(info->gpa);
+       if (!mmu) {
+               printk("%s: can't get mmu for addr 0x%08x\n", __func__, 
(u32)info->gpa);
+               return -EINVAL;
+       }
+
+    *r = readl(mmu->mem_map + ((u32)(info->gpa) - mmu->mem_start));
+
+    return 1;
+}
+
+static int mmu_mmio_write(struct vcpu *v, mmio_info_t *info)
+{
+       struct domain *dom = v->domain;
+       struct mmu_info *mmu = NULL;
+    struct cpu_user_regs *regs = guest_cpu_user_regs();
+    register_t *r = select_user_reg(regs, info->dabt.reg);
+       int res;
+
+       mmu = mmu_lookup(info->gpa);
+       if (!mmu) {
+               printk("%s: can't get mmu for addr 0x%08x\n", __func__, 
(u32)info->gpa);
+               return -EINVAL;
+       }
+
+       /*
+        * make sure that user register is written first in this function
+        * following calls may expect valid data in it
+        */
+    writel(*r, mmu->mem_map + ((u32)(info->gpa) - mmu->mem_start));
+
+       res = mmu_trap_write_access(dom, mmu, info);
+       if (res)
+               return res;
+
+    return 1;
+}
+
+static int mmu_init(struct mmu_info *mmu, u32 data)
+{
+       ASSERT(mmu);
+       ASSERT(!mmu->mem_map);
+       ASSERT(!mmu->pagetable);
+
+    mmu->mem_map = ioremap(mmu->mem_start, mmu->mem_size);
+       if (!mmu->mem_map) {
+               printk("%s: %s failed to map memory\n",  __func__, mmu->name);
+               return -EINVAL;
+       }
+
+       printk("%s: %s ipu_map = 0x%pS\n", __func__, mmu->name, 
_p(mmu->mem_map));
+
+       mmu->pagetable = xzalloc_bytes(IOPGD_TABLE_SIZE);
+       if (!mmu->pagetable) {
+               printk("%s: %s failed to alloc private pagetable\n",
+                          __func__, mmu->name);
+               return -ENOMEM;
+       }
+
+       printk("%s: %s private pagetable %lu bytes\n",
+                  __func__, mmu->name, IOPGD_TABLE_SIZE);
+
+       return 0;
+}
+
+static int mmu_init_all(void)
+{
+       int res;
+
+       res = mmu_for_each(mmu_init, 0);
+       if (res) {
+               printk("%s error during init %d\n", __func__, res);
+               return res;
+       }
+
+       return 0;
+}
+
+const struct mmio_handler mmu_mmio_handler = {
+       .check_handler = mmu_mmio_check,
+       .read_handler  = mmu_mmio_read,
+       .write_handler = mmu_mmio_write,
+};
+
+__initcall(mmu_init_all);
-- 
1.7.9.5


_______________________________________________
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®.