diff options
author | Russell King <linux@arm.linux.org.uk> | 2009-11-07 14:54:58 +0100 |
---|---|---|
committer | Hari Kanigeri <h-kanigeri2@ti.com> | 2010-04-06 14:14:25 -0500 |
commit | 7b13ecb6caddb3403d8c53f775ffe396cf48db36 (patch) | |
tree | 04c2474cfc2b2661c8bb8e65507de970cd5f9430 | |
parent | 368e009e54508fb11a12933962fe6ab780abe12d (diff) |
ARM: UNOFFICIAL_USER_DMA_API
ARM: UNOFFICIAL_USER_DMA_API
Signed-off-by: Russell King <linux@arm.linux.org.uk>
-rw-r--r-- | arch/arm/Kconfig | 19 | ||||
-rw-r--r-- | arch/arm/configs/omap_4430sdp_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/include/asm/unistd.h | 10 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 18 | ||||
-rw-r--r-- | arch/arm/mm/dma-mapping.c | 83 |
5 files changed, 130 insertions, 2 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 03d7519a75fa..9f6e9b58bf96 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1268,6 +1268,25 @@ config UACCESS_WITH_MEMCPY However, if the CPU data cache is using a write-allocate mode, this option is unlikely to provide any performance gain. +config UNOFFICIAL_USER_DMA_API + bool "Enable unofficial user DMA API (READ HELP TEXT)" + depends on EXPERIMENTAL + help + This option enables the exposure of the kernel's three DMA cache + coherence functions to userspace via three ARM private syscalls. + + This API is not officially supported; it is a stop gap measure + to allow developers to achieve their goals. It doesn't take + account of any DMA restrictions which may be in the system, and + makes no attempt to work around those. + + The user is entirely responsible for coordinating the use of this + API with DMA activity and CPU snooping activity. Improper use + of this API can result in random data corruption, especially if + the memory contains DMA scatterlists. + + Use of this API will taint the kernel. + endmenu menu "Boot options" diff --git a/arch/arm/configs/omap_4430sdp_defconfig b/arch/arm/configs/omap_4430sdp_defconfig index 54d5edb0ee15..c900c4897813 100644 --- a/arch/arm/configs/omap_4430sdp_defconfig +++ b/arch/arm/configs/omap_4430sdp_defconfig @@ -255,7 +255,7 @@ CONFIG_ARM_L1_CACHE_SHIFT=5 # CONFIG_ARM_ERRATA_460075 is not set CONFIG_PL310_ERRATA_588369=y CONFIG_ARM_GIC=y - +CONFIG_UNOFFICIAL_USER_DMA_API=y # # Bus support # diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h index 4e506d09e5f9..1298ea98909b 100644 --- a/arch/arm/include/asm/unistd.h +++ b/arch/arm/include/asm/unistd.h @@ -406,7 +406,17 @@ * *NOTE*: This is a ghost syscall private to the kernel. Only the * __kuser_cmpxchg code in entry-armv.S should be aware of its * existence. Don't ever use this from user code. +======= + * These are temporary interfaces; they are a stop gap until we get + * a proper solution to DMA. These won't always work for every + * device. Only use these IF you *really* know what you're doing. + * Don't be surprised if they go away in later kernels. +>>>>>>> ARM: UNOFFICIAL_USER_DMA_API:arch/arm/include/asm/unistd.h */ +#define __ARM_NR_temp_dma_inv_range (__ARM_NR_BASE+0x0007fd) +#define __ARM_NR_temp_dma_clean_range (__ARM_NR_BASE+0x0007fe) +#define __ARM_NR_temp_dma_flush_range (__ARM_NR_BASE+0x0007ff) + #ifdef __KERNEL__ #define __ARM_NR_cmpxchg (__ARM_NR_BASE+0x00fff0) #endif diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 23d76738ff7b..10098ed778e5 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -422,7 +422,7 @@ static int bad_syscall(int n, struct pt_regs *regs) return regs->ARM_r0; } -static inline void +static void do_cache_op(unsigned long start, unsigned long end, int flags) { struct mm_struct *mm = current->active_mm; @@ -517,6 +517,22 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) #endif return 0; +#ifdef CONFIG_UNOFFICIAL_USER_DMA_API + /* + * These are temporary interfaces; they are a stop gap until we get + * a proper solution to DMA. These won't always work for every + * device. Only use these IF you *really* know what you're doing. + * Don't be surprised if they go away in later kernels. + */ + case NR(temp_dma_inv_range): + case NR(temp_dma_clean_range): + case NR(temp_dma_flush_range): + { + extern int temp_user_dma_op(unsigned long, unsigned long, int); + return temp_user_dma_op(regs->ARM_r0, regs->ARM_r1, no & 3); + } +#endif + #ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG /* * Atomically store r1 in *r2 if *r2 is equal to r0 for user space. diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 64daef2173bd..940d44b3c59d 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -614,3 +614,86 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, } } EXPORT_SYMBOL(dma_sync_sg_for_device); + +#ifdef CONFIG_UNOFFICIAL_USER_DMA_API +int temp_user_dma_op(unsigned long start, unsigned long end, int op) +{ + struct mm_struct *mm = current->active_mm; + void (*inner_op)(const void *, const void *); + void (*outer_op)(unsigned long, unsigned long); + + if (!test_taint(TAINT_USER)) { + printk(KERN_WARNING "%s: using unofficial user DMA API, kernel tainted.\n", + current->comm); + add_taint(TAINT_USER); + } + + switch (op) { + case 1: + inner_op = dmac_inv_range; + outer_op = outer_inv_range; + break; + case 2: + inner_op = dmac_clean_range; + outer_op = outer_clean_range; + break; + case 3: + inner_op = dmac_flush_range; + outer_op = outer_flush_range; + break; + default: + return -EINVAL; + } + + if (end < start) + return -EINVAL; + + down_read(&mm->mmap_sem); + do { + struct vm_area_struct *vma = find_vma(mm, start); + + if (!vma || start < vma->vm_start || + vma->vm_flags & (VM_IO | VM_PFNMAP)) { + up_read(&mm->mmap_sem); + return -EFAULT; + } + + do { + unsigned long e = (start | ~PAGE_MASK) + 1; + struct page *page; + + if (e > end) + e = end; + + page = follow_page(vma, start, FOLL_GET); + if (IS_ERR(page)) { + up_read(&mm->mmap_sem); + return PTR_ERR(page); + } + + if (page) { + unsigned long phys; + + /* + * This flushes the userspace address - which + * is not what this API was intended to do. + * Things may go astray as a result. + */ + inner_op((void *)start, (void *)e); + + /* + * Now handle the L2 cache. + */ + phys = page_to_phys(page) + (start & ~PAGE_MASK); + outer_op(phys, phys + e - start); + + put_page(page); + } + start = e; + } while (start < end && start < vma->vm_end); + } while (start < end); + up_read(&mm->mmap_sem); + + return 0; +} +#endif |