diff options
Diffstat (limited to 'arch/openrisc/kernel')
-rw-r--r-- | arch/openrisc/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/openrisc/kernel/asm-offsets.c | 6 | ||||
-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 |
5 files changed, 82 insertions, 202 deletions
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/asm-offsets.c b/arch/openrisc/kernel/asm-offsets.c index 1a242a0d7583..ddb736855863 100644 --- a/arch/openrisc/kernel/asm-offsets.c +++ b/arch/openrisc/kernel/asm-offsets.c @@ -34,15 +34,11 @@ #include <linux/mm.h> #include <linux/io.h> #include <linux/thread_info.h> +#include <linux/kbuild.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/processor.h> -#define DEFINE(sym, val) \ - asm volatile("\n->" #sym " %0 " #val : : "i" (val)) - -#define BLANK() asm volatile("\n->" : : ) - int main(void) { /* offsets into the task_struct */ 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 -} |