summaryrefslogtreecommitdiffstats
path: root/arch/blackfin/kernel/process.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2010-01-05 09:17:33 +0900
committerTejun Heo <tj@kernel.org>2010-01-05 09:17:33 +0900
commit32032df6c2f6c9c6b2ada2ce42322231824f70c2 (patch)
treeb1ce838a37044bb38dfc128e2116ca35630e629a /arch/blackfin/kernel/process.c
parent22b737f4c75197372d64afc6ed1bccd58c00e549 (diff)
parentc5974b835a909ff15c3b7e6cf6789b5eb919f419 (diff)
Merge branch 'master' into percpu
Conflicts: arch/powerpc/platforms/pseries/hvCall.S include/linux/percpu.h
Diffstat (limited to 'arch/blackfin/kernel/process.c')
-rw-r--r--arch/blackfin/kernel/process.c130
1 files changed, 78 insertions, 52 deletions
diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c
index f5b28618964..b56b0e485e0 100644
--- a/arch/blackfin/kernel/process.c
+++ b/arch/blackfin/kernel/process.c
@@ -1,30 +1,9 @@
/*
- * File: arch/blackfin/kernel/process.c
- * Based on:
- * Author:
+ * Blackfin architecture-dependent process handling
*
- * Created:
- * Description: Blackfin architecture-dependent process handling.
+ * Copyright 2004-2009 Analog Devices Inc.
*
- * Modified:
- * Copyright 2004-2006 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * Licensed under the GPL-2 or later
*/
#include <linux/module.h>
@@ -172,7 +151,7 @@ void start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_
regs->pc = new_ip;
if (current->mm)
regs->p5 = current->mm->start_data;
-#ifdef CONFIG_SMP
+#ifndef CONFIG_SMP
task_thread_info(current)->l1_task_info.stack_start =
(void *)current->mm->context.stack_start;
task_thread_info(current)->l1_task_info.lowest_sp = (void *)new_sp;
@@ -236,22 +215,18 @@ copy_thread(unsigned long clone_flags,
/*
* sys_execve() executes a new program.
*/
-
asmlinkage int sys_execve(char __user *name, char __user * __user *argv, char __user * __user *envp)
{
int error;
char *filename;
struct pt_regs *regs = (struct pt_regs *)((&name) + 6);
- lock_kernel();
filename = getname(name);
error = PTR_ERR(filename);
if (IS_ERR(filename))
- goto out;
+ return error;
error = do_execve(filename, argv, envp, regs);
putname(filename);
- out:
- unlock_kernel();
return error;
}
@@ -283,9 +258,12 @@ void finish_atomic_sections (struct pt_regs *regs)
int __user *up0 = (int __user *)regs->p0;
switch (regs->pc) {
+ default:
+ /* not in middle of an atomic step, so resume like normal */
+ return;
+
case ATOMIC_XCHG32 + 2:
put_user(regs->r1, up0);
- regs->pc = ATOMIC_XCHG32 + 4;
break;
case ATOMIC_CAS32 + 2:
@@ -293,7 +271,6 @@ void finish_atomic_sections (struct pt_regs *regs)
if (regs->r0 == regs->r1)
case ATOMIC_CAS32 + 6:
put_user(regs->r2, up0);
- regs->pc = ATOMIC_CAS32 + 8;
break;
case ATOMIC_ADD32 + 2:
@@ -301,7 +278,6 @@ void finish_atomic_sections (struct pt_regs *regs)
/* fall through */
case ATOMIC_ADD32 + 4:
put_user(regs->r0, up0);
- regs->pc = ATOMIC_ADD32 + 6;
break;
case ATOMIC_SUB32 + 2:
@@ -309,7 +285,6 @@ void finish_atomic_sections (struct pt_regs *regs)
/* fall through */
case ATOMIC_SUB32 + 4:
put_user(regs->r0, up0);
- regs->pc = ATOMIC_SUB32 + 6;
break;
case ATOMIC_IOR32 + 2:
@@ -317,7 +292,6 @@ void finish_atomic_sections (struct pt_regs *regs)
/* fall through */
case ATOMIC_IOR32 + 4:
put_user(regs->r0, up0);
- regs->pc = ATOMIC_IOR32 + 6;
break;
case ATOMIC_AND32 + 2:
@@ -325,7 +299,6 @@ void finish_atomic_sections (struct pt_regs *regs)
/* fall through */
case ATOMIC_AND32 + 4:
put_user(regs->r0, up0);
- regs->pc = ATOMIC_AND32 + 6;
break;
case ATOMIC_XOR32 + 2:
@@ -333,9 +306,15 @@ void finish_atomic_sections (struct pt_regs *regs)
/* fall through */
case ATOMIC_XOR32 + 4:
put_user(regs->r0, up0);
- regs->pc = ATOMIC_XOR32 + 6;
break;
}
+
+ /*
+ * We've finished the atomic section, and the only thing left for
+ * userspace is to do a RTS, so we might as well handle that too
+ * since we need to update the PC anyways.
+ */
+ regs->pc = regs->rets;
}
static inline
@@ -357,12 +336,58 @@ int in_mem_const(unsigned long addr, unsigned long size,
{
return in_mem_const_off(addr, size, 0, const_addr, const_size);
}
-#define IN_ASYNC(bnum, bctlnum) \
+#define ASYNC_ENABLED(bnum, bctlnum) \
({ \
- (bfin_read_EBIU_AMGCTL() & 0xe) < ((bnum + 1) << 1) ? -EFAULT : \
- bfin_read_EBIU_AMBCTL##bctlnum() & B##bnum##RDYEN ? -EFAULT : \
- BFIN_MEM_ACCESS_CORE; \
+ (bfin_read_EBIU_AMGCTL() & 0xe) < ((bnum + 1) << 1) ? 0 : \
+ bfin_read_EBIU_AMBCTL##bctlnum() & B##bnum##RDYEN ? 0 : \
+ 1; \
})
+/*
+ * We can't read EBIU banks that aren't enabled or we end up hanging
+ * on the access to the async space. Make sure we validate accesses
+ * that cross async banks too.
+ * 0 - found, but unusable
+ * 1 - found & usable
+ * 2 - not found
+ */
+static
+int in_async(unsigned long addr, unsigned long size)
+{
+ if (addr >= ASYNC_BANK0_BASE && addr < ASYNC_BANK0_BASE + ASYNC_BANK0_SIZE) {
+ if (!ASYNC_ENABLED(0, 0))
+ return 0;
+ if (addr + size <= ASYNC_BANK0_BASE + ASYNC_BANK0_SIZE)
+ return 1;
+ size -= ASYNC_BANK0_BASE + ASYNC_BANK0_SIZE - addr;
+ addr = ASYNC_BANK0_BASE + ASYNC_BANK0_SIZE;
+ }
+ if (addr >= ASYNC_BANK1_BASE && addr < ASYNC_BANK1_BASE + ASYNC_BANK1_SIZE) {
+ if (!ASYNC_ENABLED(1, 0))
+ return 0;
+ if (addr + size <= ASYNC_BANK1_BASE + ASYNC_BANK1_SIZE)
+ return 1;
+ size -= ASYNC_BANK1_BASE + ASYNC_BANK1_SIZE - addr;
+ addr = ASYNC_BANK1_BASE + ASYNC_BANK1_SIZE;
+ }
+ if (addr >= ASYNC_BANK2_BASE && addr < ASYNC_BANK2_BASE + ASYNC_BANK2_SIZE) {
+ if (!ASYNC_ENABLED(2, 1))
+ return 0;
+ if (addr + size <= ASYNC_BANK2_BASE + ASYNC_BANK2_SIZE)
+ return 1;
+ size -= ASYNC_BANK2_BASE + ASYNC_BANK2_SIZE - addr;
+ addr = ASYNC_BANK2_BASE + ASYNC_BANK2_SIZE;
+ }
+ if (addr >= ASYNC_BANK3_BASE && addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE) {
+ if (ASYNC_ENABLED(3, 1))
+ return 0;
+ if (addr + size <= ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE)
+ return 1;
+ return 0;
+ }
+
+ /* not within async bounds */
+ return 2;
+}
int bfin_mem_access_type(unsigned long addr, unsigned long size)
{
@@ -399,17 +424,11 @@ int bfin_mem_access_type(unsigned long addr, unsigned long size)
if (addr >= SYSMMR_BASE)
return BFIN_MEM_ACCESS_CORE_ONLY;
- /* We can't read EBIU banks that aren't enabled or we end up hanging
- * on the access to the async space.
- */
- if (in_mem_const(addr, size, ASYNC_BANK0_BASE, ASYNC_BANK0_SIZE))
- return IN_ASYNC(0, 0);
- if (in_mem_const(addr, size, ASYNC_BANK1_BASE, ASYNC_BANK1_SIZE))
- return IN_ASYNC(1, 0);
- if (in_mem_const(addr, size, ASYNC_BANK2_BASE, ASYNC_BANK2_SIZE))
- return IN_ASYNC(2, 1);
- if (in_mem_const(addr, size, ASYNC_BANK3_BASE, ASYNC_BANK3_SIZE))
- return IN_ASYNC(3, 1);
+ switch (in_async(addr, size)) {
+ case 0: return -EFAULT;
+ case 1: return BFIN_MEM_ACCESS_CORE;
+ case 2: /* fall through */;
+ }
if (in_mem_const(addr, size, BOOT_ROM_START, BOOT_ROM_LENGTH))
return BFIN_MEM_ACCESS_CORE;
@@ -426,6 +445,8 @@ __attribute__((l1_text))
/* Return 1 if access to memory range is OK, 0 otherwise */
int _access_ok(unsigned long addr, unsigned long size)
{
+ int aret;
+
if (size == 0)
return 1;
/* Check that things do not wrap around */
@@ -475,6 +496,11 @@ int _access_ok(unsigned long addr, unsigned long size)
if (in_mem_const(addr, size, COREB_L1_DATA_B_START, COREB_L1_DATA_B_LENGTH))
return 1;
#endif
+
+ aret = in_async(addr, size);
+ if (aret < 2)
+ return aret;
+
if (in_mem_const_off(addr, size, _ebss_l2 - _stext_l2, L2_START, L2_LENGTH))
return 1;