Blackfin: add new cacheflush syscall

Flushing caches sometimes requires anomaly workarounds which require
supervisor-only insns.  Normally we don't need to flush caches from
userspace so this isn't a problem, but when gcc generates trampolines
on the stack, we do.

So add a new syscall for gcc to use modeled after the mips version.

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
diff --git a/arch/blackfin/include/asm/Kbuild b/arch/blackfin/include/asm/Kbuild
index d9eb29e..9e7c537 100644
--- a/arch/blackfin/include/asm/Kbuild
+++ b/arch/blackfin/include/asm/Kbuild
@@ -1,4 +1,5 @@
 include include/asm-generic/Kbuild.asm
 
 header-y += bfin_sport.h
+header-y += cachectl.h
 header-y += fixed_code.h
diff --git a/arch/blackfin/include/asm/cachectl.h b/arch/blackfin/include/asm/cachectl.h
new file mode 100644
index 0000000..03255df
--- /dev/null
+++ b/arch/blackfin/include/asm/cachectl.h
@@ -0,0 +1,20 @@
+/*
+ * based on the mips/cachectl.h
+ *
+ * Copyright 2010 Analog Devices Inc.
+ * Copyright (C) 1994, 1995, 1996 by Ralf Baechle
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef	_ASM_CACHECTL
+#define	_ASM_CACHECTL
+
+/*
+ * Options for cacheflush system call
+ */
+#define	ICACHE	(1<<0)		/* flush instruction cache        */
+#define	DCACHE	(1<<1)		/* writeback and flush data cache */
+#define	BCACHE	(ICACHE|DCACHE)	/* flush both caches              */
+
+#endif	/* _ASM_CACHECTL */
diff --git a/arch/blackfin/include/asm/ptrace.h b/arch/blackfin/include/asm/ptrace.h
index aaa1c6c..832d7c0 100644
--- a/arch/blackfin/include/asm/ptrace.h
+++ b/arch/blackfin/include/asm/ptrace.h
@@ -113,6 +113,9 @@
 /* common code demands this function */
 #define ptrace_disable(child) user_disable_single_step(child)
 
+extern int is_user_addr_valid(struct task_struct *child,
+			      unsigned long start, unsigned long len);
+
 /*
  * Get the address of the live pt_regs for the specified task.
  * These are saved onto the top kernel stack when the process
diff --git a/arch/blackfin/include/asm/unistd.h b/arch/blackfin/include/asm/unistd.h
index 14fcd25..928ae97 100644
--- a/arch/blackfin/include/asm/unistd.h
+++ b/arch/blackfin/include/asm/unistd.h
@@ -392,8 +392,9 @@
 #define __NR_fanotify_init	371
 #define __NR_fanotify_mark	372
 #define __NR_prlimit64		373
+#define __NR_cacheflush		374
 
-#define __NR_syscall		374
+#define __NR_syscall		375
 #define NR_syscalls		__NR_syscall
 
 /* Old optional stuff no one actually uses */
diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c
index d890c1e..b358393 100644
--- a/arch/blackfin/kernel/ptrace.c
+++ b/arch/blackfin/kernel/ptrace.c
@@ -114,8 +114,8 @@
 /*
  * check that an address falls within the bounds of the target process's memory mappings
  */
-static inline int is_user_addr_valid(struct task_struct *child,
-				     unsigned long start, unsigned long len)
+int
+is_user_addr_valid(struct task_struct *child, unsigned long start, unsigned long len)
 {
 	struct vm_area_struct *vma;
 	struct sram_list_struct *sraml;
diff --git a/arch/blackfin/kernel/sys_bfin.c b/arch/blackfin/kernel/sys_bfin.c
index bdc1e2f..89448ed 100644
--- a/arch/blackfin/kernel/sys_bfin.c
+++ b/arch/blackfin/kernel/sys_bfin.c
@@ -21,6 +21,8 @@
 
 #include <asm/cacheflush.h>
 #include <asm/dma.h>
+#include <asm/cachectl.h>
+#include <asm/ptrace.h>
 
 asmlinkage void *sys_sram_alloc(size_t size, unsigned long flags)
 {
@@ -70,3 +72,16 @@
 
 	return ret;
 }
+
+SYSCALL_DEFINE3(cacheflush, unsigned long, addr, unsigned long, len, int, op)
+{
+	if (is_user_addr_valid(current, addr, len) != 0)
+		return -EINVAL;
+
+	if (op & DCACHE)
+		blackfin_dcache_flush_range(addr, addr + len);
+	if (op & ICACHE)
+		blackfin_icache_flush_range(addr, addr + len);
+
+	return 0;
+}
diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S
index 16ea779..2ca915e 100644
--- a/arch/blackfin/mach-common/entry.S
+++ b/arch/blackfin/mach-common/entry.S
@@ -1737,6 +1737,7 @@
 	.long _sys_fanotify_init
 	.long _sys_fanotify_mark
 	.long _sys_prlimit64
+	.long _sys_cacheflush
 
 	.rept NR_syscalls-(.-_sys_call_table)/4
 	.long _sys_ni_syscall