summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <linux@arm.linux.org.uk>2009-11-07 14:54:58 +0100
committerHari Kanigeri <h-kanigeri2@ti.com>2010-04-06 14:14:25 -0500
commit7b13ecb6caddb3403d8c53f775ffe396cf48db36 (patch)
tree04c2474cfc2b2661c8bb8e65507de970cd5f9430
parent368e009e54508fb11a12933962fe6ab780abe12d (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/Kconfig19
-rw-r--r--arch/arm/configs/omap_4430sdp_defconfig2
-rw-r--r--arch/arm/include/asm/unistd.h10
-rw-r--r--arch/arm/kernel/traps.c18
-rw-r--r--arch/arm/mm/dma-mapping.c83
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