diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-12 12:22:13 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-12 12:22:13 -0800 |
commit | 9977d9b379cb77e0f67bd6f4563618106e58e11d (patch) | |
tree | 0191accfddf578edb52c69c933d64521e3dce297 /arch/openrisc | |
parent | cf4af01221579a4e895f43dbfc47598fbfc5a731 (diff) | |
parent | 541880d9a2c7871f6370071d55aa6662d329c51e (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/signal
Pull big execve/kernel_thread/fork unification series from Al Viro:
"All architectures are converted to new model. Quite a bit of that
stuff is actually shared with architecture trees; in such cases it's
literally shared branch pulled by both, not a cherry-pick.
A lot of ugliness and black magic is gone (-3KLoC total in this one):
- kernel_thread()/kernel_execve()/sys_execve() redesign.
We don't do syscalls from kernel anymore for either kernel_thread()
or kernel_execve():
kernel_thread() is essentially clone(2) with callback run before we
return to userland, the callbacks either never return or do
successful do_execve() before returning.
kernel_execve() is a wrapper for do_execve() - it doesn't need to
do transition to user mode anymore.
As a result kernel_thread() and kernel_execve() are
arch-independent now - they live in kernel/fork.c and fs/exec.c
resp. sys_execve() is also in fs/exec.c and it's completely
architecture-independent.
- daemonize() is gone, along with its parts in fs/*.c
- struct pt_regs * is no longer passed to do_fork/copy_process/
copy_thread/do_execve/search_binary_handler/->load_binary/do_coredump.
- sys_fork()/sys_vfork()/sys_clone() unified; some architectures
still need wrappers (ones with callee-saved registers not saved in
pt_regs on syscall entry), but the main part of those suckers is in
kernel/fork.c now."
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/signal: (113 commits)
do_coredump(): get rid of pt_regs argument
print_fatal_signal(): get rid of pt_regs argument
ptrace_signal(): get rid of unused arguments
get rid of ptrace_signal_deliver() arguments
new helper: signal_pt_regs()
unify default ptrace_signal_deliver
flagday: kill pt_regs argument of do_fork()
death to idle_regs()
don't pass regs to copy_process()
flagday: don't pass regs to copy_thread()
bfin: switch to generic vfork, get rid of pointless wrappers
xtensa: switch to generic clone()
openrisc: switch to use of generic fork and clone
unicore32: switch to generic clone(2)
score: switch to generic fork/vfork/clone
c6x: sanitize copy_thread(), get rid of clone(2) wrapper, switch to generic clone()
take sys_fork/sys_vfork/sys_clone prototypes to linux/syscalls.h
mn10300: switch to generic fork/vfork/clone
h8300: switch to generic fork/vfork/clone
tile: switch to generic clone()
...
Conflicts:
arch/microblaze/include/asm/Kbuild
Diffstat (limited to 'arch/openrisc')
-rw-r--r-- | arch/openrisc/Kconfig | 2 | ||||
-rw-r--r-- | arch/openrisc/include/asm/processor.h | 2 | ||||
-rw-r--r-- | arch/openrisc/include/asm/syscalls.h | 7 | ||||
-rw-r--r-- | arch/openrisc/include/uapi/asm/unistd.h | 4 | ||||
-rw-r--r-- | arch/openrisc/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/openrisc/kernel/entry.S | 55 | ||||
-rw-r--r-- | arch/openrisc/kernel/process.c | 164 | ||||
-rw-r--r-- | arch/openrisc/kernel/sys_or32.c | 57 |
8 files changed, 94 insertions, 199 deletions
diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index 05f2ba41ff1a..e7f1a2993f78 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -22,6 +22,8 @@ config OPENRISC select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER select MODULES_USE_ELF_RELA + select GENERIC_KERNEL_THREAD + select GENERIC_KERNEL_EXECVE config MMU def_bool y diff --git a/arch/openrisc/include/asm/processor.h b/arch/openrisc/include/asm/processor.h index 43decdbdb2ed..33691380608e 100644 --- a/arch/openrisc/include/asm/processor.h +++ b/arch/openrisc/include/asm/processor.h @@ -81,8 +81,6 @@ struct thread_struct { #define KSTK_ESP(tsk) (task_pt_regs(tsk)->sp) -extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); - void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp); void release_thread(struct task_struct *); unsigned long get_wchan(struct task_struct *p); diff --git a/arch/openrisc/include/asm/syscalls.h b/arch/openrisc/include/asm/syscalls.h index 84a978af44d7..8ee816812a9e 100644 --- a/arch/openrisc/include/asm/syscalls.h +++ b/arch/openrisc/include/asm/syscalls.h @@ -24,4 +24,11 @@ asmlinkage long sys_or1k_atomic(unsigned long type, unsigned long *v1, #include <asm-generic/syscalls.h> +asmlinkage long __sys_clone(unsigned long clone_flags, unsigned long newsp, + void __user *parent_tid, void __user *child_tid, int tls); +asmlinkage long __sys_fork(void); + +#define sys_clone __sys_clone +#define sys_fork __sys_fork + #endif /* __ASM_OPENRISC_SYSCALLS_H */ diff --git a/arch/openrisc/include/uapi/asm/unistd.h b/arch/openrisc/include/uapi/asm/unistd.h index 437bdbb61b14..5082b8066325 100644 --- a/arch/openrisc/include/uapi/asm/unistd.h +++ b/arch/openrisc/include/uapi/asm/unistd.h @@ -20,6 +20,10 @@ #define sys_mmap2 sys_mmap_pgoff +#define __ARCH_WANT_SYS_EXECVE +#define __ARCH_WANT_SYS_FORK +#define __ARCH_WANT_SYS_CLONE + #include <asm-generic/unistd.h> #define __NR_or1k_atomic __NR_arch_specific_syscall diff --git a/arch/openrisc/kernel/Makefile b/arch/openrisc/kernel/Makefile index e1ee0fa2bbda..35f92ce51c24 100644 --- a/arch/openrisc/kernel/Makefile +++ b/arch/openrisc/kernel/Makefile @@ -5,7 +5,7 @@ extra-y := head.o vmlinux.lds obj-y := setup.o idle.o or32_ksyms.o process.o dma.o \ - traps.o time.o irq.o entry.o ptrace.o signal.o sys_or32.o \ + traps.o time.o irq.o entry.o ptrace.o signal.o \ sys_call_table.o obj-$(CONFIG_MODULES) += module.o diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S index ddfcaa828b0e..5e5b30601bbf 100644 --- a/arch/openrisc/kernel/entry.S +++ b/arch/openrisc/kernel/entry.S @@ -894,6 +894,16 @@ ENTRY(ret_from_fork) l.jal schedule_tail l.nop + /* Check if we are a kernel thread */ + l.sfeqi r20,0 + l.bf 1f + l.nop + + /* ...we are a kernel thread so invoke the requested callback */ + l.jalr r20 + l.or r3,r22,r0 + +1: /* _syscall_returns expect r11 to contain return value */ l.lwz r11,PT_GPR11(r1) @@ -915,26 +925,6 @@ ENTRY(ret_from_fork) l.j _syscall_return l.nop -/* Since syscalls don't save call-clobbered registers, the args to - * kernel_thread_helper will need to be passed through callee-saved - * registers and copied to the parameter registers when the thread - * begins running. - * - * See arch/openrisc/kernel/process.c: - * The args are passed as follows: - * arg1 (r3) : passed in r20 - * arg2 (r4) : passed in r22 - */ - -ENTRY(_kernel_thread_helper) - l.or r3,r20,r0 - l.or r4,r22,r0 - l.movhi r31,hi(kernel_thread_helper) - l.ori r31,r31,lo(kernel_thread_helper) - l.jr r31 - l.nop - - /* ========================================================[ switch ] === */ /* @@ -1044,8 +1034,13 @@ ENTRY(_switch) /* Unwind stack to pre-switch state */ l.addi r1,r1,(INT_FRAME_SIZE) - /* Return via the link-register back to where we 'came from', where that can be - * either schedule() or return_from_fork()... */ + /* Return via the link-register back to where we 'came from', where + * that may be either schedule(), ret_from_fork(), or + * ret_from_kernel_thread(). If we are returning to a new thread, + * we are expected to have set up the arg to schedule_tail already, + * hence we do so here unconditionally: + */ + l.lwz r3,TI_STACK(r3) /* Load 'prev' as schedule_tail arg */ l.jr r9 l.nop @@ -1076,22 +1071,18 @@ _fork_save_extra_regs_and_call: l.jr r29 l.sw PT_GPR28(r1),r28 -ENTRY(sys_clone) - l.movhi r29,hi(_sys_clone) - l.ori r29,r29,lo(_sys_clone) +ENTRY(__sys_clone) + l.movhi r29,hi(sys_clone) + l.ori r29,r29,lo(sys_clone) l.j _fork_save_extra_regs_and_call l.addi r7,r1,0 -ENTRY(sys_fork) - l.movhi r29,hi(_sys_fork) - l.ori r29,r29,lo(_sys_fork) +ENTRY(__sys_fork) + l.movhi r29,hi(sys_fork) + l.ori r29,r29,lo(sys_fork) l.j _fork_save_extra_regs_and_call l.addi r3,r1,0 -ENTRY(sys_execve) - l.j _sys_execve - l.addi r6,r1,0 - ENTRY(sys_sigaltstack) l.j _sys_sigaltstack l.addi r5,r1,0 diff --git a/arch/openrisc/kernel/process.c b/arch/openrisc/kernel/process.c index c35f3ab1a8d3..00c233bf0d06 100644 --- a/arch/openrisc/kernel/process.c +++ b/arch/openrisc/kernel/process.c @@ -109,66 +109,83 @@ void release_thread(struct task_struct *dead_task) */ extern asmlinkage void ret_from_fork(void); +/* + * copy_thread + * @clone_flags: flags + * @usp: user stack pointer or fn for kernel thread + * @arg: arg to fn for kernel thread; always NULL for userspace thread + * @p: the newly created task + * @regs: CPU context to copy for userspace thread; always NULL for kthread + * + * At the top of a newly initialized kernel stack are two stacked pt_reg + * structures. The first (topmost) is the userspace context of the thread. + * The second is the kernelspace context of the thread. + * + * A kernel thread will not be returning to userspace, so the topmost pt_regs + * struct can be uninitialized; it _does_ need to exist, though, because + * a kernel thread can become a userspace thread by doing a kernel_execve, in + * which case the topmost context will be initialized and used for 'returning' + * to userspace. + * + * The second pt_reg struct needs to be initialized to 'return' to + * ret_from_fork. A kernel thread will need to set r20 to the address of + * a function to call into (with arg in r22); userspace threads need to set + * r20 to NULL in which case ret_from_fork will just continue a return to + * userspace. + * + * A kernel thread 'fn' may return; this is effectively what happens when + * kernel_execve is called. In that case, the userspace pt_regs must have + * been initialized (which kernel_execve takes care of, see start_thread + * below); ret_from_fork will then continue its execution causing the + * 'kernel thread' to return to userspace as a userspace thread. + */ + int copy_thread(unsigned long clone_flags, unsigned long usp, - unsigned long unused, struct task_struct *p, struct pt_regs *regs) + unsigned long arg, struct task_struct *p) { - struct pt_regs *childregs; + struct pt_regs *userregs; struct pt_regs *kregs; unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE; - struct thread_info *ti; unsigned long top_of_kernel_stack; top_of_kernel_stack = sp; p->set_child_tid = p->clear_child_tid = NULL; - /* Copy registers */ - /* redzone */ - sp -= STACK_FRAME_OVERHEAD; + /* Locate userspace context on stack... */ + sp -= STACK_FRAME_OVERHEAD; /* redzone */ sp -= sizeof(struct pt_regs); - childregs = (struct pt_regs *)sp; + userregs = (struct pt_regs *) sp; - /* Copy parent registers */ - *childregs = *regs; + /* ...and kernel context */ + sp -= STACK_FRAME_OVERHEAD; /* redzone */ + sp -= sizeof(struct pt_regs); + kregs = (struct pt_regs *)sp; - if ((childregs->sr & SPR_SR_SM) == 1) { - /* for kernel thread, set `current_thread_info' - * and stackptr in new task - */ - childregs->sp = (unsigned long)task_stack_page(p) + THREAD_SIZE; - childregs->gpr[10] = (unsigned long)task_thread_info(p); + if (unlikely(p->flags & PF_KTHREAD)) { + memset(kregs, 0, sizeof(struct pt_regs)); + kregs->gpr[20] = usp; /* fn, kernel thread */ + kregs->gpr[22] = arg; } else { - childregs->sp = usp; - } - - childregs->gpr[11] = 0; /* Result from fork() */ + *userregs = *current_pt_regs(); - /* - * The way this works is that at some point in the future - * some task will call _switch to switch to the new task. - * That will pop off the stack frame created below and start - * the new task running at ret_from_fork. The new task will - * do some house keeping and then return from the fork or clone - * system call, using the stack frame created above. - */ - /* redzone */ - sp -= STACK_FRAME_OVERHEAD; - sp -= sizeof(struct pt_regs); - kregs = (struct pt_regs *)sp; + if (usp) + userregs->sp = usp; + userregs->gpr[11] = 0; /* Result from fork() */ - ti = task_thread_info(p); - ti->ksp = sp; + kregs->gpr[20] = 0; /* Userspace thread */ + } - /* kregs->sp must store the location of the 'pre-switch' kernel stack - * pointer... for a newly forked process, this is simply the top of - * the kernel stack. + /* + * _switch wants the kernel stack page in pt_regs->sp so that it + * can restore it to thread_info->ksp... see _switch for details. */ kregs->sp = top_of_kernel_stack; - kregs->gpr[3] = (unsigned long)current; /* arg to schedule_tail */ - kregs->gpr[10] = (unsigned long)task_thread_info(p); kregs->gpr[9] = (unsigned long)ret_from_fork; + task_thread_info(p)->ksp = (unsigned long)kregs; + return 0; } @@ -177,16 +194,14 @@ copy_thread(unsigned long clone_flags, unsigned long usp, */ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) { - unsigned long sr = regs->sr & ~SPR_SR_SM; + unsigned long sr = mfspr(SPR_SR) & ~SPR_SR_SM; set_fs(USER_DS); - memset(regs->gpr, 0, sizeof(regs->gpr)); + memset(regs, 0, sizeof(struct pt_regs)); regs->pc = pc; regs->sr = sr; regs->sp = sp; - -/* printk("start thread, ksp = %lx\n", current_thread_info()->ksp);*/ } /* Fill in the fpu structure for a core dump. */ @@ -237,74 +252,9 @@ void dump_elf_thread(elf_greg_t *dest, struct pt_regs* regs) dest[35] = 0; } -extern void _kernel_thread_helper(void); - -void __noreturn kernel_thread_helper(int (*fn) (void *), void *arg) -{ - do_exit(fn(arg)); -} - -/* - * Create a kernel thread. - */ -int kernel_thread(int (*fn) (void *), void *arg, unsigned long flags) -{ - struct pt_regs regs; - - memset(®s, 0, sizeof(regs)); - - regs.gpr[20] = (unsigned long)fn; - regs.gpr[22] = (unsigned long)arg; - regs.sr = mfspr(SPR_SR); - regs.pc = (unsigned long)_kernel_thread_helper; - - return do_fork(flags | CLONE_VM | CLONE_UNTRACED, - 0, ®s, 0, NULL, NULL); -} - -/* - * sys_execve() executes a new program. - */ -asmlinkage long _sys_execve(const char __user *name, - const char __user * const __user *argv, - const char __user * const __user *envp, - struct pt_regs *regs) -{ - int error; - struct filename *filename; - - filename = getname(name); - error = PTR_ERR(filename); - - if (IS_ERR(filename)) - goto out; - - error = do_execve(filename->name, argv, envp, regs); - putname(filename); - -out: - return error; -} - unsigned long get_wchan(struct task_struct *p) { /* TODO */ return 0; } - -int kernel_execve(const char *filename, char *const argv[], char *const envp[]) -{ - register long __res asm("r11") = __NR_execve; - register long __a asm("r3") = (long)(filename); - register long __b asm("r4") = (long)(argv); - register long __c asm("r5") = (long)(envp); - __asm__ volatile ("l.sys 1" - : "=r" (__res), "=r"(__a), "=r"(__b), "=r"(__c) - : "0"(__res), "1"(__a), "2"(__b), "3"(__c) - : "r6", "r7", "r8", "r12", "r13", "r15", - "r17", "r19", "r21", "r23", "r25", "r27", - "r29", "r31"); - __asm__ volatile ("l.nop"); - return __res; -} diff --git a/arch/openrisc/kernel/sys_or32.c b/arch/openrisc/kernel/sys_or32.c deleted file mode 100644 index 57060084c0cc..000000000000 --- a/arch/openrisc/kernel/sys_or32.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * OpenRISC sys_or32.c - * - * Linux architectural port borrowing liberally from similar works of - * others. All original copyrights apply as per the original source - * declaration. - * - * Modifications for the OpenRISC architecture: - * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com> - * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se> - * - * 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 file contains various random system calls that - * have a non-standard calling sequence on some platforms. - * Since we don't have to do any backwards compatibility, our - * versions are done in the most "normal" way possible. - */ - -#include <linux/errno.h> -#include <linux/syscalls.h> -#include <linux/mm.h> - -#include <asm/syscalls.h> - -/* These are secondary entry points as the primary entry points are defined in - * entry.S where we add the 'regs' parameter value - */ - -asmlinkage long _sys_clone(unsigned long clone_flags, unsigned long newsp, - int __user *parent_tid, int __user *child_tid, - struct pt_regs *regs) -{ - long ret; - - /* FIXME: Is alignment necessary? */ - /* newsp = ALIGN(newsp, 4); */ - - if (!newsp) - newsp = regs->sp; - - ret = do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid); - - return ret; -} - -asmlinkage int _sys_fork(struct pt_regs *regs) -{ -#ifdef CONFIG_MMU - return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL); -#else - return -EINVAL; -#endif -} |