[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [RFC v01 1/3] arm: omap: introduce iommu module
On Wed, 22 Jan 2014, Andrii Tseglytskyi wrote: > 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) I think that omap_iommu should be a platform specific driver, and it should hook into a set of platform specific mmio_handlers instead of using the generic mmio_handler structure. > 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 It should probably live under xen/arch/arm/platforms. > @@ -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) { Xen uses a different coding style from Linux, see CODING_STYLE. > + 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); Do you need to flush the dcache here? > + 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); So you are flushing the dcache all at once at the end, probably better this way. > + 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; > +} I wonder if we actually need to trap guest accesses in all cases: if we leave the GPU/IPU in Dom0, mapped 1:1, then we don't need any traps. Maybe we can find a way to detect that so that we can avoid trapping and translating in that case. > +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 > _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |