Commit f4091322 authored by Al Viro's avatar Al Viro

Merge branches 'no-rebases', 'arch-avr32', 'arch-blackfin', 'arch-cris',...

Merge branches 'no-rebases', 'arch-avr32', 'arch-blackfin', 'arch-cris', 'arch-h8300', 'arch-m32r', 'arch-mn10300', 'arch-score', 'arch-sh' and 'arch-powerpc' into for-next
...@@ -7,6 +7,8 @@ config ARM64 ...@@ -7,6 +7,8 @@ config ARM64
select GENERIC_IOMAP select GENERIC_IOMAP
select GENERIC_IRQ_PROBE select GENERIC_IRQ_PROBE
select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW
select GENERIC_KERNEL_EXECVE
select GENERIC_KERNEL_THREAD
select GENERIC_SMP_IDLE_THREAD select GENERIC_SMP_IDLE_THREAD
select GENERIC_TIME_VSYSCALL select GENERIC_TIME_VSYSCALL
select HARDIRQS_SW_RESEND select HARDIRQS_SW_RESEND
......
...@@ -128,11 +128,6 @@ unsigned long get_wchan(struct task_struct *p); ...@@ -128,11 +128,6 @@ unsigned long get_wchan(struct task_struct *p);
extern struct task_struct *cpu_switch_to(struct task_struct *prev, extern struct task_struct *cpu_switch_to(struct task_struct *prev,
struct task_struct *next); struct task_struct *next);
/*
* Create a new kernel thread
*/
extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
#define task_pt_regs(p) \ #define task_pt_regs(p) \
((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1) ((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1)
......
...@@ -23,18 +23,16 @@ ...@@ -23,18 +23,16 @@
/* /*
* System call wrappers implemented in kernel/entry.S. * System call wrappers implemented in kernel/entry.S.
*/ */
asmlinkage long sys_execve_wrapper(const char __user *filename,
const char __user *const __user *argv,
const char __user *const __user *envp);
asmlinkage long sys_clone_wrapper(unsigned long clone_flags,
unsigned long newsp,
void __user *parent_tid,
unsigned long tls_val,
void __user *child_tid);
asmlinkage long sys_rt_sigreturn_wrapper(void); asmlinkage long sys_rt_sigreturn_wrapper(void);
asmlinkage long sys_sigaltstack_wrapper(const stack_t __user *uss, asmlinkage long sys_sigaltstack_wrapper(const stack_t __user *uss,
stack_t __user *uoss); stack_t __user *uoss);
/*
* AArch64 sys_clone implementation has a different prototype than the generic
* one (additional TLS value argument).
*/
#define sys_clone sys_clone
#include <asm-generic/syscalls.h> #include <asm-generic/syscalls.h>
#endif /* __ASM_SYSCALLS_H */ #endif /* __ASM_SYSCALLS_H */
...@@ -25,4 +25,5 @@ ...@@ -25,4 +25,5 @@
#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_COMPAT_SYS_SENDFILE #define __ARCH_WANT_COMPAT_SYS_SENDFILE
#endif #endif
#define __ARCH_WANT_SYS_EXECVE
#include <uapi/asm/unistd.h> #include <uapi/asm/unistd.h>
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
__SYSCALL(0, sys_restart_syscall) __SYSCALL(0, sys_restart_syscall)
__SYSCALL(1, sys_exit) __SYSCALL(1, sys_exit)
__SYSCALL(2, compat_sys_fork_wrapper) __SYSCALL(2, compat_sys_fork)
__SYSCALL(3, sys_read) __SYSCALL(3, sys_read)
__SYSCALL(4, sys_write) __SYSCALL(4, sys_write)
__SYSCALL(5, compat_sys_open) __SYSCALL(5, compat_sys_open)
...@@ -32,7 +32,7 @@ __SYSCALL(7, sys_ni_syscall) /* 7 was sys_waitpid */ ...@@ -32,7 +32,7 @@ __SYSCALL(7, sys_ni_syscall) /* 7 was sys_waitpid */
__SYSCALL(8, sys_creat) __SYSCALL(8, sys_creat)
__SYSCALL(9, sys_link) __SYSCALL(9, sys_link)
__SYSCALL(10, sys_unlink) __SYSCALL(10, sys_unlink)
__SYSCALL(11, compat_sys_execve_wrapper) __SYSCALL(11, compat_sys_execve)
__SYSCALL(12, sys_chdir) __SYSCALL(12, sys_chdir)
__SYSCALL(13, sys_ni_syscall) /* 13 was sys_time */ __SYSCALL(13, sys_ni_syscall) /* 13 was sys_time */
__SYSCALL(14, sys_mknod) __SYSCALL(14, sys_mknod)
...@@ -141,7 +141,7 @@ __SYSCALL(116, compat_sys_sysinfo) ...@@ -141,7 +141,7 @@ __SYSCALL(116, compat_sys_sysinfo)
__SYSCALL(117, sys_ni_syscall) /* 117 was sys_ipc */ __SYSCALL(117, sys_ni_syscall) /* 117 was sys_ipc */
__SYSCALL(118, sys_fsync) __SYSCALL(118, sys_fsync)
__SYSCALL(119, compat_sys_sigreturn_wrapper) __SYSCALL(119, compat_sys_sigreturn_wrapper)
__SYSCALL(120, compat_sys_clone_wrapper) __SYSCALL(120, sys_clone)
__SYSCALL(121, sys_setdomainname) __SYSCALL(121, sys_setdomainname)
__SYSCALL(122, sys_newuname) __SYSCALL(122, sys_newuname)
__SYSCALL(123, sys_ni_syscall) /* 123 was sys_modify_ldt */ __SYSCALL(123, sys_ni_syscall) /* 123 was sys_modify_ldt */
...@@ -211,7 +211,7 @@ __SYSCALL(186, compat_sys_sigaltstack_wrapper) ...@@ -211,7 +211,7 @@ __SYSCALL(186, compat_sys_sigaltstack_wrapper)
__SYSCALL(187, compat_sys_sendfile) __SYSCALL(187, compat_sys_sendfile)
__SYSCALL(188, sys_ni_syscall) /* 188 reserved */ __SYSCALL(188, sys_ni_syscall) /* 188 reserved */
__SYSCALL(189, sys_ni_syscall) /* 189 reserved */ __SYSCALL(189, sys_ni_syscall) /* 189 reserved */
__SYSCALL(190, compat_sys_vfork_wrapper) __SYSCALL(190, compat_sys_vfork)
__SYSCALL(191, compat_sys_getrlimit) /* SuS compliant getrlimit */ __SYSCALL(191, compat_sys_getrlimit) /* SuS compliant getrlimit */
__SYSCALL(192, sys_mmap_pgoff) __SYSCALL(192, sys_mmap_pgoff)
__SYSCALL(193, compat_sys_truncate64_wrapper) __SYSCALL(193, compat_sys_truncate64_wrapper)
......
...@@ -594,7 +594,7 @@ work_resched: ...@@ -594,7 +594,7 @@ work_resched:
/* /*
* "slow" syscall return path. * "slow" syscall return path.
*/ */
ENTRY(ret_to_user) ret_to_user:
disable_irq // disable interrupts disable_irq // disable interrupts
ldr x1, [tsk, #TI_FLAGS] ldr x1, [tsk, #TI_FLAGS]
and x2, x1, #_TIF_WORK_MASK and x2, x1, #_TIF_WORK_MASK
...@@ -611,7 +611,10 @@ ENDPROC(ret_to_user) ...@@ -611,7 +611,10 @@ ENDPROC(ret_to_user)
*/ */
ENTRY(ret_from_fork) ENTRY(ret_from_fork)
bl schedule_tail bl schedule_tail
get_thread_info tsk cbz x19, 1f // not a kernel thread
mov x0, x20
blr x19
1: get_thread_info tsk
b ret_to_user b ret_to_user
ENDPROC(ret_from_fork) ENDPROC(ret_from_fork)
...@@ -673,16 +676,6 @@ __sys_trace_return: ...@@ -673,16 +676,6 @@ __sys_trace_return:
/* /*
* Special system call wrappers. * Special system call wrappers.
*/ */
ENTRY(sys_execve_wrapper)
mov x3, sp
b sys_execve
ENDPROC(sys_execve_wrapper)
ENTRY(sys_clone_wrapper)
mov x5, sp
b sys_clone
ENDPROC(sys_clone_wrapper)
ENTRY(sys_rt_sigreturn_wrapper) ENTRY(sys_rt_sigreturn_wrapper)
mov x0, sp mov x0, sp
b sys_rt_sigreturn b sys_rt_sigreturn
......
...@@ -240,27 +240,41 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, ...@@ -240,27 +240,41 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
struct pt_regs *childregs = task_pt_regs(p); struct pt_regs *childregs = task_pt_regs(p);
unsigned long tls = p->thread.tp_value; unsigned long tls = p->thread.tp_value;
memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context));
if (likely(regs)) {
*childregs = *regs; *childregs = *regs;
childregs->regs[0] = 0; childregs->regs[0] = 0;
if (is_compat_thread(task_thread_info(p))) {
if (is_compat_thread(task_thread_info(p))) if (stack_start)
childregs->compat_sp = stack_start; childregs->compat_sp = stack_start;
else { } else {
/* /*
* Read the current TLS pointer from tpidr_el0 as it may be * Read the current TLS pointer from tpidr_el0 as it may be
* out-of-sync with the saved value. * out-of-sync with the saved value.
*/ */
asm("mrs %0, tpidr_el0" : "=r" (tls)); asm("mrs %0, tpidr_el0" : "=r" (tls));
if (stack_start) {
/* 16-byte aligned stack mandatory on AArch64 */
if (stack_start & 15)
return -EINVAL;
childregs->sp = stack_start; childregs->sp = stack_start;
} }
}
memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context)); /*
p->thread.cpu_context.sp = (unsigned long)childregs; * If a TLS pointer was passed to clone (4th argument), use it
p->thread.cpu_context.pc = (unsigned long)ret_from_fork; * for the new thread.
*/
/* If a TLS pointer was passed to clone, use that for the new thread. */
if (clone_flags & CLONE_SETTLS) if (clone_flags & CLONE_SETTLS)
tls = regs->regs[3]; tls = regs->regs[3];
} else {
memset(childregs, 0, sizeof(struct pt_regs));
childregs->pstate = PSR_MODE_EL1h;
p->thread.cpu_context.x19 = stack_start;
p->thread.cpu_context.x20 = stk_sz;
}
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
p->thread.cpu_context.sp = (unsigned long)childregs;
p->thread.tp_value = tls; p->thread.tp_value = tls;
ptrace_hw_copy_thread(p); ptrace_hw_copy_thread(p);
...@@ -309,43 +323,6 @@ struct task_struct *__switch_to(struct task_struct *prev, ...@@ -309,43 +323,6 @@ struct task_struct *__switch_to(struct task_struct *prev,
return last; return last;
} }
/*
* Shuffle the argument into the correct register before calling the
* thread function. x1 is the thread argument, x2 is the pointer to
* the thread function, and x3 points to the exit function.
*/
extern void kernel_thread_helper(void);
asm( ".section .text\n"
" .align\n"
" .type kernel_thread_helper, #function\n"
"kernel_thread_helper:\n"
" mov x0, x1\n"
" mov x30, x3\n"
" br x2\n"
" .size kernel_thread_helper, . - kernel_thread_helper\n"
" .previous");
#define kernel_thread_exit do_exit
/*
* Create a kernel thread.
*/
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
regs.regs[1] = (unsigned long)arg;
regs.regs[2] = (unsigned long)fn;
regs.regs[3] = (unsigned long)kernel_thread_exit;
regs.pc = (unsigned long)kernel_thread_helper;
regs.pstate = PSR_MODE_EL1h;
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}
EXPORT_SYMBOL(kernel_thread);
unsigned long get_wchan(struct task_struct *p) unsigned long get_wchan(struct task_struct *p)
{ {
struct stackframe frame; struct stackframe frame;
......
...@@ -31,79 +31,11 @@ ...@@ -31,79 +31,11 @@
*/ */
asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp, asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp,
int __user *parent_tidptr, unsigned long tls_val, int __user *parent_tidptr, unsigned long tls_val,
int __user *child_tidptr, struct pt_regs *regs) int __user *child_tidptr)
{ {
if (!newsp) return do_fork(clone_flags, newsp, current_pt_regs(), 0,
newsp = regs->sp; parent_tidptr, child_tidptr);
/* 16-byte aligned stack mandatory on AArch64 */
if (newsp & 15)
return -EINVAL;
return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
}
/*
* sys_execve() executes a new program.
*/
asmlinkage long sys_execve(const char __user *filenamei,
const char __user *const __user *argv,
const char __user *const __user *envp,
struct pt_regs *regs)
{
long error;
struct filename *filename;
filename = getname(filenamei);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename->name, argv, envp, regs);
putname(filename);
out:
return error;
}
int kernel_execve(const char *filename,
const char *const argv[],
const char *const envp[])
{
struct pt_regs regs;
int ret;
memset(&regs, 0, sizeof(struct pt_regs));
ret = do_execve(filename,
(const char __user *const __user *)argv,
(const char __user *const __user *)envp, &regs);
if (ret < 0)
goto out;
/*
* Save argc to the register structure for userspace.
*/
regs.regs[0] = ret;
/*
* We were successful. We won't be returning to our caller, but
* instead to user space by manipulating the kernel stack.
*/
asm( "add x0, %0, %1\n\t"
"mov x1, %2\n\t"
"mov x2, %3\n\t"
"bl memmove\n\t" /* copy regs to top of stack */
"mov x27, #0\n\t" /* not a syscall */
"mov x28, %0\n\t" /* thread structure */
"mov sp, x0\n\t" /* reposition stack pointer */
"b ret_to_user"
:
: "r" (current_thread_info()),
"Ir" (THREAD_START_SP - sizeof(regs)),
"r" (&regs),
"Ir" (sizeof(regs))
: "x0", "x1", "x2", "x27", "x28", "x30", "memory");
out:
return ret;
} }
EXPORT_SYMBOL(kernel_execve);
asmlinkage long sys_mmap(unsigned long addr, unsigned long len, asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long prot, unsigned long flags,
...@@ -118,8 +50,6 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len, ...@@ -118,8 +50,6 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
/* /*
* Wrappers to pass the pt_regs argument. * Wrappers to pass the pt_regs argument.
*/ */
#define sys_execve sys_execve_wrapper
#define sys_clone sys_clone_wrapper
#define sys_rt_sigreturn sys_rt_sigreturn_wrapper #define sys_rt_sigreturn sys_rt_sigreturn_wrapper
#define sys_sigaltstack sys_sigaltstack_wrapper #define sys_sigaltstack sys_sigaltstack_wrapper
......
...@@ -26,25 +26,6 @@ ...@@ -26,25 +26,6 @@
/* /*
* System call wrappers for the AArch32 compatibility layer. * System call wrappers for the AArch32 compatibility layer.
*/ */
compat_sys_fork_wrapper:
mov x0, sp
b compat_sys_fork
ENDPROC(compat_sys_fork_wrapper)
compat_sys_vfork_wrapper:
mov x0, sp
b compat_sys_vfork
ENDPROC(compat_sys_vfork_wrapper)
compat_sys_execve_wrapper:
mov x3, sp
b compat_sys_execve
ENDPROC(compat_sys_execve_wrapper)
compat_sys_clone_wrapper:
mov x5, sp
b compat_sys_clone
ENDPROC(compat_sys_clone_wrapper)
compat_sys_sigreturn_wrapper: compat_sys_sigreturn_wrapper:
mov x0, sp mov x0, sp
......
...@@ -28,43 +28,15 @@ ...@@ -28,43 +28,15 @@
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/unistd32.h> #include <asm/unistd32.h>
asmlinkage int compat_sys_fork(struct pt_regs *regs) asmlinkage int compat_sys_fork(void)
{ {
return do_fork(SIGCHLD, regs->compat_sp, regs, 0, NULL, NULL); return do_fork(SIGCHLD, 0, current_pt_regs(), 0, NULL, NULL);
} }
asmlinkage int compat_sys_clone(unsigned long clone_flags, unsigned long newsp, asmlinkage int compat_sys_vfork(void)
int __user *parent_tidptr, int tls_val,
int __user *child_tidptr, struct pt_regs *regs)
{ {
if (!newsp) return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0,
newsp = regs->compat_sp; current_pt_regs(), 0, NULL, NULL);
return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
}
asmlinkage int compat_sys_vfork(struct pt_regs *regs)
{
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->compat_sp,
regs, 0, NULL, NULL);
}
asmlinkage int compat_sys_execve(const char __user *filenamei,
compat_uptr_t argv, compat_uptr_t envp,
struct pt_regs *regs)
{
int error;
struct filename *filename;
filename = getname(filenamei);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = compat_do_execve(filename->name, compat_ptr(argv),
compat_ptr(envp), regs);
putname(filename);
out:
return error;
} }
asmlinkage int compat_sys_sched_rr_get_interval(compat_pid_t pid, asmlinkage int compat_sys_sched_rr_get_interval(compat_pid_t pid,
......
...@@ -17,6 +17,8 @@ config AVR32 ...@@ -17,6 +17,8 @@ config AVR32
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select HAVE_MOD_ARCH_SPECIFIC select HAVE_MOD_ARCH_SPECIFIC
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
help help
AVR32 is a high-performance 32-bit RISC microprocessor core, AVR32 is a high-performance 32-bit RISC microprocessor core,
designed for cost-sensitive embedded applications, with particular designed for cost-sensitive embedded applications, with particular
......
...@@ -142,9 +142,6 @@ struct task_struct; ...@@ -142,9 +142,6 @@ struct task_struct;
/* Free all resources held by a thread */ /* Free all resources held by a thread */
extern void release_thread(struct task_struct *); extern void release_thread(struct task_struct *);
/* Create a kernel thread without removing it from tasklists */
extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
/* Return saved PC of a blocked thread */ /* Return saved PC of a blocked thread */
#define thread_saved_pc(tsk) ((tsk)->thread.cpu_context.pc) #define thread_saved_pc(tsk) ((tsk)->thread.cpu_context.pc)
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#define __ARCH_WANT_SYS_GETPGRP #define __ARCH_WANT_SYS_GETPGRP
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_SYS_EXECVE
/* /*
* "Conditional" syscalls * "Conditional" syscalls
......
...@@ -7,7 +7,7 @@ extra-y := head.o vmlinux.lds ...@@ -7,7 +7,7 @@ extra-y := head.o vmlinux.lds
obj-$(CONFIG_SUBARCH_AVR32B) += entry-avr32b.o obj-$(CONFIG_SUBARCH_AVR32B) += entry-avr32b.o
obj-y += syscall_table.o syscall-stubs.o irq.o obj-y += syscall_table.o syscall-stubs.o irq.o
obj-y += setup.o traps.o ocd.o ptrace.o obj-y += setup.o traps.o ocd.o ptrace.o
obj-y += signal.o sys_avr32.o process.o time.o obj-y += signal.o process.o time.o
obj-y += switch_to.o cpu.o obj-y += switch_to.o cpu.o
obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o
obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_KPROBES) += kprobes.o
......
...@@ -251,13 +251,15 @@ syscall_badsys: ...@@ -251,13 +251,15 @@ syscall_badsys:
.global ret_from_fork .global ret_from_fork
ret_from_fork: ret_from_fork:
call schedule_tail call schedule_tail
mov r12, 0
rjmp syscall_return
/* check for syscall tracing */ .global ret_from_kernel_thread
get_thread_info r0 ret_from_kernel_thread:
ld.w r1, r0[TI_flags] call schedule_tail
andl r1, _TIF_ALLWORK_MASK, COH mov r12, r0
brne syscall_exit_work mov lr, r2 /* syscall_return */
rjmp syscall_exit_cont mov pc, r1
syscall_trace_enter: syscall_trace_enter:
pushm r8-r12 pushm r8-r12
......
...@@ -68,44 +68,6 @@ void machine_restart(char *cmd) ...@@ -68,44 +68,6 @@ void machine_restart(char *cmd)
while (1) ; while (1) ;
} }
/*
* PC is actually discarded when returning from a system call -- the
* return address must be stored in LR. This function will make sure
* LR points to do_exit before starting the thread.
*
* Also, when returning from fork(), r12 is 0, so we must copy the
* argument as well.
*
* r0 : The argument to the main thread function
* r1 : The address of do_exit
* r2 : The address of the main thread function
*/
asmlinkage extern void kernel_thread_helper(void);
__asm__(" .type kernel_thread_helper, @function\n"
"kernel_thread_helper:\n"
" mov r12, r0\n"
" mov lr, r2\n"
" mov pc, r1\n"
" .size kernel_thread_helper, . - kernel_thread_helper");
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
regs.r0 = (unsigned long)arg;
regs.r1 = (unsigned long)fn;
regs.r2 = (unsigned long)do_exit;
regs.lr = (unsigned long)kernel_thread_helper;
regs.pc = (unsigned long)kernel_thread_helper;
regs.sr = MODE_SUPERVISOR;
return do_fork(flags | CLONE_VM | CLONE_UNTRACED,
0, &regs, 0, NULL, NULL);
}
EXPORT_SYMBOL(kernel_thread);
/* /*
* Free current thread data structures etc * Free current thread data structures etc
*/ */
...@@ -332,26 +294,31 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) ...@@ -332,26 +294,31 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
} }
asmlinkage void ret_from_fork(void); asmlinkage void ret_from_fork(void);
asmlinkage void ret_from_kernel_thread(void);
asmlinkage void syscall_return(void);
int copy_thread(unsigned long clone_flags, unsigned long usp, int copy_thread(unsigned long clone_flags, unsigned long usp,
unsigned long unused, unsigned long arg,
struct task_struct *p, struct pt_regs *regs) struct task_struct *p, struct pt_regs *regs)
{ {
struct pt_regs *childregs; struct pt_regs *childregs = task_pt_regs(p);
childregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long)task_stack_page(p))) - 1; if (unlikely(!regs)) {
memset(childregs, 0, sizeof(struct pt_regs));
p->thread.cpu_context.r0 = arg;
p->thread.cpu_context.r1 = usp; /* fn */
p->thread.cpu_context.r2 = syscall_return;
p->thread.cpu_context.pc = (unsigned long)ret_from_kernel_thread;
childregs->sr = MODE_SUPERVISOR;
} else {
*childregs = *regs; *childregs = *regs;
if (user_mode(regs))
childregs->sp = usp; childregs->sp = usp;
else
childregs->sp = (unsigned long)task_stack_page(p) + THREAD_SIZE;
childregs->r12 = 0; /* Set return value for child */ childregs->r12 = 0; /* Set return value for child */
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
}
p->thread.cpu_context.sr = MODE_SUPERVISOR | SR_GM; p->thread.cpu_context.sr = MODE_SUPERVISOR | SR_GM;
p->thread.cpu_context.ksp = (unsigned long)childregs; p->thread.cpu_context.ksp = (unsigned long)childregs;
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
clear_tsk_thread_flag(p, TIF_DEBUG); clear_tsk_thread_flag(p, TIF_DEBUG);
if ((clone_flags & CLONE_PTRACE) && test_thread_flag(TIF_DEBUG)) if ((clone_flags & CLONE_PTRACE) && test_thread_flag(TIF_DEBUG))
...@@ -382,27 +349,6 @@ asmlinkage int sys_vfork(struct pt_regs *regs) ...@@ -382,27 +349,6 @@ asmlinkage int sys_vfork(struct pt_regs *regs)
0, NULL, NULL); 0, NULL, NULL);
} }
asmlinkage int sys_execve(const char __user *ufilename,
const char __user *const __user *uargv,
const char __user *const __user *uenvp,
struct pt_regs *regs)
{
int error;
struct filename *filename;
filename = getname(ufilename);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename->name, uargv, uenvp, regs);
putname(filename);
out:
return error;
}
/* /*
* This function is supposed to answer the question "who called * This function is supposed to answer the question "who called
* schedule()?" * schedule()?"
......
/*
* Copyright (C) 2004-2006 Atmel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/unistd.h>
int kernel_execve(const char *file,
const char *const *argv,
const char *const *envp)
{
register long scno asm("r8") = __NR_execve;
register long sc1 asm("r12") = (long)file;
register long sc2 asm("r11") = (long)argv;
register long sc3 asm("r10") = (long)envp;
asm volatile("scall"
: "=r"(sc1)
: "r"(scno), "0"(sc1), "r"(sc2), "r"(sc3)
: "cc", "memory");
return sc1;
}
...@@ -50,12 +50,6 @@ __sys_vfork: ...@@ -50,12 +50,6 @@ __sys_vfork:
mov r12, sp mov r12, sp
rjmp sys_vfork rjmp sys_vfork
.global __sys_execve
.type __sys_execve,@function
__sys_execve:
mov r9, sp
rjmp sys_execve
.global __sys_mmap2 .global __sys_mmap2
.type __sys_mmap2,@function .type __sys_mmap2,@function
__sys_mmap2: __sys_mmap2:
......
...@@ -24,7 +24,7 @@ sys_call_table: ...@@ -24,7 +24,7 @@ sys_call_table:
.long sys_creat .long sys_creat
.long sys_link .long sys_link
.long sys_unlink /* 10 */ .long sys_unlink /* 10 */
.long __sys_execve .long sys_execve
.long sys_chdir .long sys_chdir
.long sys_time .long sys_time
.long sys_mknod .long sys_mknod
......
...@@ -45,6 +45,8 @@ config BLACKFIN ...@@ -45,6 +45,8 @@ config BLACKFIN
select ARCH_USES_GETTIMEOFFSET if !GENERIC_CLOCKEVENTS select ARCH_USES_GETTIMEOFFSET if !GENERIC_CLOCKEVENTS
select HAVE_MOD_ARCH_SPECIFIC select HAVE_MOD_ARCH_SPECIFIC
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
config GENERIC_CSUM config GENERIC_CSUM
def_bool y def_bool y
......
...@@ -75,8 +75,6 @@ static inline void release_thread(struct task_struct *dead_task) ...@@ -75,8 +75,6 @@ static inline void release_thread(struct task_struct *dead_task)
{ {
} }
extern int kernel_thread(int (*fn) (void *), void *arg, unsigned long flags);
/* /*
* Free current thread data structures etc.. * Free current thread data structures etc..
*/ */
......
...@@ -446,6 +446,7 @@ ...@@ -446,6 +446,7 @@
#define __ARCH_WANT_SYS_NICE #define __ARCH_WANT_SYS_NICE
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_SYS_EXECVE
/* /*
* "Conditional" syscalls * "Conditional" syscalls
......
...@@ -46,22 +46,16 @@ ENTRY(_ret_from_fork) ...@@ -46,22 +46,16 @@ ENTRY(_ret_from_fork)
SP += -12; SP += -12;
pseudo_long_call _schedule_tail, p5; pseudo_long_call _schedule_tail, p5;
SP += 12; SP += 12;
r0 = [sp + PT_IPEND]; p1 = [sp++];
cc = bittst(r0,1); r0 = [sp++];
if cc jump .Lin_kernel; cc = p1 == 0;
if cc jump .Lfork;
sp += -12;
call (p1);
sp += 12;
.Lfork:
RESTORE_CONTEXT RESTORE_CONTEXT
rti; rti;
.Lin_kernel:
bitclr(r0,1);
[sp + PT_IPEND] = r0;
/* do a 'fake' RTI by jumping to [RETI]
* to avoid clearing supervisor mode in child
*/
r0 = [sp + PT_PC];
[sp + PT_P0] = r0;
RESTORE_ALL_SYS
jump (p0);
ENDPROC(_ret_from_fork) ENDPROC(_ret_from_fork)
ENTRY(_sys_vfork) ENTRY(_sys_vfork)
......
...@@ -101,40 +101,6 @@ void cpu_idle(void) ...@@ -101,40 +101,6 @@ void cpu_idle(void)
} }
} }
/*
* This gets run with P1 containing the
* function to call, and R1 containing
* the "args". Note P0 is clobbered on the way here.
*/
void kernel_thread_helper(void);
__asm__(".section .text\n"
".align 4\n"
"_kernel_thread_helper:\n\t"
"\tsp += -12;\n\t"
"\tr0 = r1;\n\t" "\tcall (p1);\n\t" "\tcall _do_exit;\n" ".previous");
/*
* Create a kernel thread.
*/
pid_t kernel_thread(int (*fn) (void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
regs.r1 = (unsigned long)arg;
regs.p1 = (unsigned long)fn;
regs.pc = (unsigned long)kernel_thread_helper;
regs.orig_p0 = -1;
/* Set bit 2 to tell ret_from_fork we should be returning to kernel
mode. */
regs.ipend = 0x8002;
__asm__ __volatile__("%0 = syscfg;":"=da"(regs.syscfg):);
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL,
NULL);
}
EXPORT_SYMBOL(kernel_thread);
/* /*
* Do necessary setup to start up a newly executed thread. * Do necessary setup to start up a newly executed thread.
* *
...@@ -193,38 +159,31 @@ copy_thread(unsigned long clone_flags, ...@@ -193,38 +159,31 @@ copy_thread(unsigned long clone_flags,
struct task_struct *p, struct pt_regs *regs) struct task_struct *p, struct pt_regs *regs)
{ {
struct pt_regs *childregs; struct pt_regs *childregs;
unsigned long *v;
childregs = (struct pt_regs *) (task_stack_page(p) + THREAD_SIZE) - 1; childregs = (struct pt_regs *) (task_stack_page(p) + THREAD_SIZE) - 1;
v = ((unsigned long *)childregs) - 2;
if (unlikely(!regs)) {
memset(childregs, 0, sizeof(struct pt_regs));
v[0] = usp;
v[1] = topstk;
childregs->orig_p0 = -1;
childregs->ipend = 0x8000;
__asm__ __volatile__("%0 = syscfg;":"=da"(childregs->syscfg):);
p->thread.usp = 0;
} else {
*childregs = *regs; *childregs = *regs;
childregs->r0 = 0; childregs->r0 = 0;
p->thread.usp = usp; p->thread.usp = usp;
p->thread.ksp = (unsigned long)childregs; v[0] = v[1] = 0;
}
p->thread.ksp = (unsigned long)v;
p->thread.pc = (unsigned long)ret_from_fork; p->thread.pc = (unsigned long)ret_from_fork;
return 0; return 0;
} }
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys_execve(const char __user *name,
const char __user *const __user *argv,
const char __user *const __user *envp)
{
int error;
struct filename *filename;
struct pt_regs *regs = (struct pt_regs *)((&name) + 6);
filename = getname(name);
error = PTR_ERR(filename);
if (IS_ERR(filename))
return error;
error = do_execve(filename->name, argv, envp, regs);
putname(filename);
return error;
}
unsigned long get_wchan(struct task_struct *p) unsigned long get_wchan(struct task_struct *p)
{ {
unsigned long fp, pc; unsigned long fp, pc;
......
...@@ -530,61 +530,6 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/ ...@@ -530,61 +530,6 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/
jump .Lsyscall_really_exit; jump .Lsyscall_really_exit;
ENDPROC(_trap) ENDPROC(_trap)
ENTRY(_kernel_execve)
link SIZEOF_PTREGS;
p0 = sp;
r3 = SIZEOF_PTREGS / 4;
r4 = 0(x);
.Lclear_regs:
[p0++] = r4;
r3 += -1;
cc = r3 == 0;
if !cc jump .Lclear_regs (bp);
p0 = sp;
sp += -16;
[sp + 12] = p0;
pseudo_long_call _do_execve, p5;
SP += 16;
cc = r0 == 0;
if ! cc jump .Lexecve_failed;
/* Success. Copy our temporary pt_regs to the top of the kernel
* stack and do a normal exception return.
*/
r1 = sp;
r0 = (-KERNEL_STACK_SIZE) (x);
r1 = r1 & r0;
p2 = r1;
p3 = [p2];
r0 = KERNEL_STACK_SIZE - 4 (z);
p1 = r0;
p1 = p1 + p2;
p0 = fp;
r4 = [p0--];
r3 = SIZEOF_PTREGS / 4;
.Lcopy_regs:
r4 = [p0--];
[p1--] = r4;
r3 += -1;
cc = r3 == 0;
if ! cc jump .Lcopy_regs (bp);
r0 = (KERNEL_STACK_SIZE - SIZEOF_PTREGS) (z);
p1 = r0;
p1 = p1 + p2;
sp = p1;
r0 = syscfg;
[SP + PT_SYSCFG] = r0;
[p3 + (TASK_THREAD + THREAD_KSP)] = sp;
RESTORE_CONTEXT;
rti;
.Lexecve_failed:
unlink;
rts;
ENDPROC(_kernel_execve)
ENTRY(_system_call) ENTRY(_system_call)
/* Store IPEND */ /* Store IPEND */
p2.l = lo(IPEND); p2.l = lo(IPEND);
......
...@@ -18,6 +18,7 @@ config C6X ...@@ -18,6 +18,7 @@ config C6X
select OF_EARLY_FLATTREE select OF_EARLY_FLATTREE
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select GENERIC_KERNEL_THREAD select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
config MMU config MMU
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
* more details. * more details.
*/ */
#define __ARCH_WANT_KERNEL_EXECVE
#define __ARCH_WANT_SYS_EXECVE #define __ARCH_WANT_SYS_EXECVE
/* Use the standard ABI for syscalls. */ /* Use the standard ABI for syscalls. */
......
...@@ -413,19 +413,9 @@ ENTRY(ret_from_kernel_thread) ...@@ -413,19 +413,9 @@ ENTRY(ret_from_kernel_thread)
0: 0:
B .S2 B10 /* call fn */ B .S2 B10 /* call fn */
LDW .D2T1 *+SP(REGS_A1+8),A4 /* get arg */ LDW .D2T1 *+SP(REGS_A1+8),A4 /* get arg */
MVKL .S2 sys_exit,B11 ADDKPC .S2 ret_from_fork_2,B3,3
MVKH .S2 sys_exit,B11
ADDKPC .S2 0f,B3,1
0:
BNOP .S2 B11,5 /* jump to sys_exit */
ENDPROC(ret_from_kernel_thread) ENDPROC(ret_from_kernel_thread)
ENTRY(ret_from_kernel_execve)
GET_THREAD_INFO A12
BNOP .S2 syscall_exit,4
ADD .D2X A4,-8,SP
ENDPROC(ret_from_kernel_execve)
;; ;;
;; These are the interrupt handlers, responsible for calling __do_IRQ() ;; These are the interrupt handlers, responsible for calling __do_IRQ()
;; int6 is used for syscalls (see _system_call entry) ;; int6 is used for syscalls (see _system_call entry)
......
...@@ -49,6 +49,8 @@ config CRIS ...@@ -49,6 +49,8 @@ config CRIS
select GENERIC_SMP_IDLE_THREAD if ETRAX_ARCH_V32 select GENERIC_SMP_IDLE_THREAD if ETRAX_ARCH_V32
select GENERIC_CMOS_UPDATE select GENERIC_CMOS_UPDATE
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
config HZ config HZ
int int
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
.globl system_call .globl system_call
.globl ret_from_intr .globl ret_from_intr
.globl ret_from_fork .globl ret_from_fork
.globl ret_from_kernel_thread
.globl resume .globl resume
.globl multiple_interrupt .globl multiple_interrupt
.globl hwbreakpoint .globl hwbreakpoint
...@@ -82,6 +83,13 @@ ret_from_fork: ...@@ -82,6 +83,13 @@ ret_from_fork:
ba ret_from_sys_call ba ret_from_sys_call
nop nop
ret_from_kernel_thread:
jsr schedule_tail
move.d $r2, $r10 ; argument is here
jsr $r1 ; call the payload
moveq 0, $r9 ; no syscall restarts, TYVM...
ba ret_from_sys_call
ret_from_intr: ret_from_intr:
;; check for resched if preemptive kernel or if we're going back to user-mode ;; check for resched if preemptive kernel or if we're going back to user-mode
;; this test matches the user_regs(regs) macro ;; this test matches the user_regs(regs) macro
...@@ -586,13 +594,6 @@ _ugdb_handle_breakpoint: ...@@ -586,13 +594,6 @@ _ugdb_handle_breakpoint:
ba do_sigtrap ; SIGTRAP the offending process. ba do_sigtrap ; SIGTRAP the offending process.
pop $dccr ; Restore dccr in delay slot. pop $dccr ; Restore dccr in delay slot.
.global kernel_execve
kernel_execve:
move.d __NR_execve, $r9
break 13
ret
nop
.data .data
hw_bp_trigs: hw_bp_trigs:
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <arch/svinto.h> #include <arch/svinto.h>
#include <linux/init.h> #include <linux/init.h>
#include <arch/system.h> #include <arch/system.h>
#include <asm/ptrace.h>
#ifdef CONFIG_ETRAX_GPIO #ifdef CONFIG_ETRAX_GPIO
void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */ void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */
...@@ -81,31 +82,6 @@ unsigned long thread_saved_pc(struct task_struct *t) ...@@ -81,31 +82,6 @@ unsigned long thread_saved_pc(struct task_struct *t)
return task_pt_regs(t)->irp; return task_pt_regs(t)->irp;
} }
static void kernel_thread_helper(void* dummy, int (*fn)(void *), void * arg)
{
fn(arg);
do_exit(-1); /* Should never be called, return bad exit value */
}
/*
* Create a kernel thread
*/
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
/* Don't use r10 since that is set to 0 in copy_thread */
regs.r11 = (unsigned long)fn;
regs.r12 = (unsigned long)arg;
regs.irp = (unsigned long)kernel_thread_helper;
regs.dccr = 1 << I_DCCR_BITNR;
/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}
/* setup the child's kernel stack with a pt_regs and switch_stack on it. /* setup the child's kernel stack with a pt_regs and switch_stack on it.
* it will be un-nested during _resume and _ret_from_sys_call when the * it will be un-nested during _resume and _ret_from_sys_call when the
* new thread is scheduled. * new thread is scheduled.
...@@ -115,30 +91,36 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) ...@@ -115,30 +91,36 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
* *
*/ */
asmlinkage void ret_from_fork(void); asmlinkage void ret_from_fork(void);
asmlinkage void ret_from_kernel_thread(void);
int copy_thread(unsigned long clone_flags, unsigned long usp, int copy_thread(unsigned long clone_flags, unsigned long usp,
unsigned long unused, unsigned long arg,
struct task_struct *p, struct pt_regs *regs) struct task_struct *p, struct pt_regs *regs)
{ {
struct pt_regs * childregs; struct pt_regs *childregs = task_pt_regs(p);
struct switch_stack *swstack; struct switch_stack *swstack = ((struct switch_stack *)childregs) - 1;
/* put the pt_regs structure at the end of the new kernel stack page and fix it up /* put the pt_regs structure at the end of the new kernel stack page and fix it up
* remember that the task_struct doubles as the kernel stack for the task * remember that the task_struct doubles as the kernel stack for the task
*/ */
childregs = task_pt_regs(p); if (unlikely(p->flags & PF_KTHREAD)) {
memset(swstack, 0,
sizeof(struct switch_stack) + sizeof(struct pt_regs));
swstack->r1 = usp;
swstack->r2 = arg;
childregs->dccr = 1 << I_DCCR_BITNR;
swstack->return_ip = (unsigned long) ret_from_kernel_thread;
p->thread.ksp = (unsigned long) swstack;
p->thread.usp = 0;
return 0;
}
*childregs = *regs; /* struct copy of pt_regs */ *childregs = *regs; /* struct copy of pt_regs */
p->set_child_tid = p->clear_child_tid = NULL;
childregs->r10 = 0; /* child returns 0 after a fork/clone */ childregs->r10 = 0; /* child returns 0 after a fork/clone */
/* put the switch stack right below the pt_regs */ /* put the switch stack right below the pt_regs */
swstack = ((struct switch_stack *)childregs) - 1;
swstack->r9 = 0; /* parameter to ret_from_sys_call, 0 == dont restart the syscall */ swstack->r9 = 0; /* parameter to ret_from_sys_call, 0 == dont restart the syscall */
/* we want to return into ret_from_sys_call after the _resume */ /* we want to return into ret_from_sys_call after the _resume */
...@@ -161,68 +143,28 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -161,68 +143,28 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
return 0; return 0;
} }
/* asmlinkage int sys_fork(void)
* Be aware of the "magic" 7th argument in the four system-calls below.
* They need the latest stackframe, which is put as the 7th argument by
* entry.S. The previous arguments are dummies or actually used, but need
* to be defined to reach the 7th argument.
*
* N.B.: Another method to get the stackframe is to use current_regs(). But
* it returns the latest stack-frame stacked when going from _user mode_ and
* some of these (at least sys_clone) are called from kernel-mode sometimes
* (for example during kernel_thread, above) and thus cannot use it. Thus,
* to be sure not to get any surprises, we use the method for the other calls
* as well.
*/
asmlinkage int sys_fork(long r10, long r11, long r12, long r13, long mof, long srp,
struct pt_regs *regs)
{ {
return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL); return do_fork(SIGCHLD, rdusp(), current_pt_regs(), 0, NULL, NULL);
} }
/* if newusp is 0, we just grab the old usp */ /* if newusp is 0, we just grab the old usp */
/* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */ /* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */
asmlinkage int sys_clone(unsigned long newusp, unsigned long flags, asmlinkage int sys_clone(unsigned long newusp, unsigned long flags,
int* parent_tid, int* child_tid, long mof, long srp, int* parent_tid, int* child_tid)
struct pt_regs *regs)
{ {
if (!newusp) if (!newusp)
newusp = rdusp(); newusp = rdusp();
return do_fork(flags, newusp, regs, 0, parent_tid, child_tid); return do_fork(flags, newusp, current_pt_regs(), 0, parent_tid, child_tid);
} }
/* vfork is a system call in i386 because of register-pressure - maybe /* vfork is a system call in i386 because of register-pressure - maybe
* we can remove it and handle it in libc but we put it here until then. * we can remove it and handle it in libc but we put it here until then.
*/ */
asmlinkage int sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp, asmlinkage int sys_vfork(void)
struct pt_regs *regs)
{
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL);
}
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys_execve(const char *fname,
const char *const *argv,
const char *const *envp,
long r13, long mof, long srp,
struct pt_regs *regs)
{ {
int error; return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), current_pt_regs(), 0, NULL, NULL);
struct filename *filename;
filename = getname(fname);
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) unsigned long get_wchan(struct task_struct *p)
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
.globl system_call .globl system_call
.globl ret_from_intr .globl ret_from_intr
.globl ret_from_fork .globl ret_from_fork
.globl ret_from_kernel_thread
.globl resume .globl resume
.globl multiple_interrupt .globl multiple_interrupt
.globl nmi_interrupt .globl nmi_interrupt
...@@ -84,6 +85,18 @@ ret_from_fork: ...@@ -84,6 +85,18 @@ ret_from_fork:
nop nop
.size ret_from_fork, . - ret_from_fork .size ret_from_fork, . - ret_from_fork
.type ret_from_kernel_thread,@function
ret_from_kernel_thread:
jsr schedule_tail
nop
move.d $r2, $r10
jsr $r1
nop
moveq 0, $r9 ; no syscall restarts, TYVM...
ba ret_from_sys_call
nop
.size ret_from_kernel_thread, . - ret_from_kernel_thread
.type ret_from_intr,@function .type ret_from_intr,@function
ret_from_intr: ret_from_intr:
;; Check for resched if preemptive kernel, or if we're going back to ;; Check for resched if preemptive kernel, or if we're going back to
...@@ -531,15 +544,6 @@ _ugdb_handle_exception: ...@@ -531,15 +544,6 @@ _ugdb_handle_exception:
ba do_sigtrap ; SIGTRAP the offending process. ba do_sigtrap ; SIGTRAP the offending process.
move.d [$sp+], $r0 ; Restore R0 in delay slot. move.d [$sp+], $r0 ; Restore R0 in delay slot.
.global kernel_execve
.type kernel_execve,@function
kernel_execve:
move.d __NR_execve, $r9
break 13
ret
nop
.size kernel_execve, . - kernel_execve
.data .data
.section .rodata,"a" .section .rodata,"a"
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <hwregs/reg_map.h> #include <hwregs/reg_map.h>
#include <hwregs/timer_defs.h> #include <hwregs/timer_defs.h>
#include <hwregs/intr_vect_defs.h> #include <hwregs/intr_vect_defs.h>
#include <asm/ptrace.h>
extern void stop_watchdog(void); extern void stop_watchdog(void);
...@@ -94,31 +95,6 @@ unsigned long thread_saved_pc(struct task_struct *t) ...@@ -94,31 +95,6 @@ unsigned long thread_saved_pc(struct task_struct *t)
return task_pt_regs(t)->erp; return task_pt_regs(t)->erp;
} }
static void
kernel_thread_helper(void* dummy, int (*fn)(void *), void * arg)
{
fn(arg);
do_exit(-1); /* Should never be called, return bad exit value. */
}
/* Create a kernel thread. */
int
kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
/* Don't use r10 since that is set to 0 in copy_thread. */
regs.r11 = (unsigned long) fn;
regs.r12 = (unsigned long) arg;
regs.erp = (unsigned long) kernel_thread_helper;
regs.ccs = 1 << (I_CCS_BITNR + CCS_SHIFT);
/* Create the new process. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}
/* /*
* Setup the child's kernel stack with a pt_regs and call switch_stack() on it. * Setup the child's kernel stack with a pt_regs and call switch_stack() on it.
* It will be unnested during _resume and _ret_from_sys_call when the new thread * It will be unnested during _resume and _ret_from_sys_call when the new thread
...@@ -129,23 +105,33 @@ kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) ...@@ -129,23 +105,33 @@ kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
*/ */
extern asmlinkage void ret_from_fork(void); extern asmlinkage void ret_from_fork(void);
extern asmlinkage void ret_from_kernel_thread(void);
int int
copy_thread(unsigned long clone_flags, unsigned long usp, copy_thread(unsigned long clone_flags, unsigned long usp,
unsigned long unused, unsigned long arg,
struct task_struct *p, struct pt_regs *regs) struct task_struct *p, struct pt_regs *regs)
{ {
struct pt_regs *childregs; struct pt_regs *childregs = task_pt_regs(p);
struct switch_stack *swstack; struct switch_stack *swstack = ((struct switch_stack *) childregs) - 1;
/* /*
* Put the pt_regs structure at the end of the new kernel stack page and * Put the pt_regs structure at the end of the new kernel stack page and
* fix it up. Note: the task_struct doubles as the kernel stack for the * fix it up. Note: the task_struct doubles as the kernel stack for the
* task. * task.
*/ */
childregs = task_pt_regs(p); if (unlikely(p->flags & PF_KTHREAD)) {
memset(swstack, 0,
sizeof(struct switch_stack) + sizeof(struct pt_regs));
swstack->r1 = usp;
swstack->r2 = arg;
childregs->ccs = 1 << (I_CCS_BITNR + CCS_SHIFT);
swstack->return_ip = (unsigned long) ret_from_kernel_thread;
p->thread.ksp = (unsigned long) swstack;
p->thread.usp = 0;
return 0;
}
*childregs = *regs; /* Struct copy of pt_regs. */ *childregs = *regs; /* Struct copy of pt_regs. */
p->set_child_tid = p->clear_child_tid = NULL;
childregs->r10 = 0; /* Child returns 0 after a fork/clone. */ childregs->r10 = 0; /* Child returns 0 after a fork/clone. */
/* Set a new TLS ? /* Set a new TLS ?
...@@ -156,7 +142,6 @@ copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -156,7 +142,6 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
} }
/* Put the switch stack right below the pt_regs. */ /* Put the switch stack right below the pt_regs. */
swstack = ((struct switch_stack *) childregs) - 1;
/* Parameter to ret_from_sys_call. 0 is don't restart the syscall. */ /* Parameter to ret_from_sys_call. 0 is don't restart the syscall. */
swstack->r9 = 0; swstack->r9 = 0;
...@@ -174,35 +159,21 @@ copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -174,35 +159,21 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
return 0; return 0;
} }
/*
* Be aware of the "magic" 7th argument in the four system-calls below.
* They need the latest stackframe, which is put as the 7th argument by
* entry.S. The previous arguments are dummies or actually used, but need
* to be defined to reach the 7th argument.
*
* N.B.: Another method to get the stackframe is to use current_regs(). But
* it returns the latest stack-frame stacked when going from _user mode_ and
* some of these (at least sys_clone) are called from kernel-mode sometimes
* (for example during kernel_thread, above) and thus cannot use it. Thus,
* to be sure not to get any surprises, we use the method for the other calls
* as well.
*/
asmlinkage int asmlinkage int
sys_fork(long r10, long r11, long r12, long r13, long mof, long srp, sys_fork(void)
struct pt_regs *regs)
{ {
return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL); return do_fork(SIGCHLD, rdusp(), current_pt_regs(), 0, NULL, NULL);
} }
/* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */ /* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */
asmlinkage int asmlinkage int
sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child_tid, sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child_tid,
unsigned long tls, long srp, struct pt_regs *regs) unsigned long tls)
{ {
if (!newusp) if (!newusp)
newusp = rdusp(); newusp = rdusp();
return do_fork(flags, newusp, regs, 0, parent_tid, child_tid); return do_fork(flags, newusp, current_pt_regs(), 0, parent_tid, child_tid);
} }
/* /*
...@@ -210,32 +181,9 @@ sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child ...@@ -210,32 +181,9 @@ sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child
* we can remove it and handle it in libc but we put it here until then. * we can remove it and handle it in libc but we put it here until then.
*/ */
asmlinkage int asmlinkage int
sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp, sys_vfork(void)
struct pt_regs *regs)
{ {
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL); return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), current_pt_regs(), 0, NULL, NULL);
}
/* sys_execve() executes a new program. */
asmlinkage int
sys_execve(const char *fname,
const char *const *argv,
const char *const *envp, long r13, long mof, long srp,
struct pt_regs *regs)
{
int error;
struct filename *filename;
filename = getname(fname);
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 unsigned long
......
...@@ -49,8 +49,6 @@ struct task_struct; ...@@ -49,8 +49,6 @@ struct task_struct;
#define task_pt_regs(task) user_regs(task_thread_info(task)) #define task_pt_regs(task) user_regs(task_thread_info(task))
#define current_regs() task_pt_regs(current) #define current_regs() task_pt_regs(current)
extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
unsigned long get_wchan(struct task_struct *p); unsigned long get_wchan(struct task_struct *p);
#define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->thread.usp) #define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->thread.usp)
......
...@@ -371,6 +371,7 @@ ...@@ -371,6 +371,7 @@
#define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_SIGPROCMASK
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_SYS_EXECVE
/* /*
* "Conditional" syscalls * "Conditional" syscalls
......
...@@ -30,7 +30,6 @@ extern void __negdi2(void); ...@@ -30,7 +30,6 @@ extern void __negdi2(void);
extern void iounmap(volatile void * __iomem); extern void iounmap(volatile void * __iomem);
/* Platform dependent support */ /* Platform dependent support */
EXPORT_SYMBOL(kernel_thread);
EXPORT_SYMBOL(get_cmos_time); EXPORT_SYMBOL(get_cmos_time);
EXPORT_SYMBOL(loops_per_usec); EXPORT_SYMBOL(loops_per_usec);
......
...@@ -8,6 +8,8 @@ config H8300 ...@@ -8,6 +8,8 @@ config H8300
select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW
select GENERIC_CPU_DEVICES select GENERIC_CPU_DEVICES
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
config SYMBOL_PREFIX config SYMBOL_PREFIX
string string
......
...@@ -107,8 +107,6 @@ static inline void release_thread(struct task_struct *dead_task) ...@@ -107,8 +107,6 @@ static inline void release_thread(struct task_struct *dead_task)
{ {
} }
extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
/* /*
* Free current thread data structures etc.. * Free current thread data structures etc..
*/ */
......
...@@ -60,6 +60,8 @@ struct pt_regs { ...@@ -60,6 +60,8 @@ struct pt_regs {
#define user_mode(regs) (!((regs)->ccr & PS_S)) #define user_mode(regs) (!((regs)->ccr & PS_S))
#define instruction_pointer(regs) ((regs)->pc) #define instruction_pointer(regs) ((regs)->pc)
#define profile_pc(regs) instruction_pointer(regs) #define profile_pc(regs) instruction_pointer(regs)
#define current_pt_regs() ((struct pt_regs *) \
(THREAD_SIZE + (unsigned long)current_thread_info()) - 1)
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* _H8300_PTRACE_H */ #endif /* _H8300_PTRACE_H */
...@@ -356,6 +356,7 @@ ...@@ -356,6 +356,7 @@
#define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_SIGPROCMASK
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_SYS_EXECVE
/* /*
* "Conditional" syscalls * "Conditional" syscalls
......
...@@ -158,6 +158,7 @@ INTERRUPTS = 128 ...@@ -158,6 +158,7 @@ INTERRUPTS = 128
.globl SYMBOL_NAME(system_call) .globl SYMBOL_NAME(system_call)
.globl SYMBOL_NAME(ret_from_exception) .globl SYMBOL_NAME(ret_from_exception)
.globl SYMBOL_NAME(ret_from_fork) .globl SYMBOL_NAME(ret_from_fork)
.globl SYMBOL_NAME(ret_from_kernel_thread)
.globl SYMBOL_NAME(ret_from_interrupt) .globl SYMBOL_NAME(ret_from_interrupt)
.globl SYMBOL_NAME(interrupt_redirect_table) .globl SYMBOL_NAME(interrupt_redirect_table)
.globl SYMBOL_NAME(sw_ksp),SYMBOL_NAME(sw_usp) .globl SYMBOL_NAME(sw_ksp),SYMBOL_NAME(sw_usp)
...@@ -330,6 +331,14 @@ SYMBOL_NAME_LABEL(ret_from_fork) ...@@ -330,6 +331,14 @@ SYMBOL_NAME_LABEL(ret_from_fork)
jsr @SYMBOL_NAME(schedule_tail) jsr @SYMBOL_NAME(schedule_tail)
jmp @SYMBOL_NAME(ret_from_exception) jmp @SYMBOL_NAME(ret_from_exception)
SYMBOL_NAME_LABEL(ret_from_kernel_thread)
mov.l er2,er0
jsr @SYMBOL_NAME(schedule_tail)
mov.l @(LER4:16,sp),er0
mov.l @(LER5:16,sp),er1
jsr @er1
jmp @SYMBOL_NAME(ret_from_exception)
SYMBOL_NAME_LABEL(resume) SYMBOL_NAME_LABEL(resume)
/* /*
* Beware - when entering resume, offset of tss is in d1, * Beware - when entering resume, offset of tss is in d1,
......
...@@ -33,7 +33,6 @@ EXPORT_SYMBOL(strncmp); ...@@ -33,7 +33,6 @@ EXPORT_SYMBOL(strncmp);
EXPORT_SYMBOL(ip_fast_csum); EXPORT_SYMBOL(ip_fast_csum);
EXPORT_SYMBOL(kernel_thread);
EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(enable_irq);
EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(disable_irq);
......
...@@ -47,6 +47,7 @@ void (*pm_power_off)(void) = NULL; ...@@ -47,6 +47,7 @@ void (*pm_power_off)(void) = NULL;
EXPORT_SYMBOL(pm_power_off); EXPORT_SYMBOL(pm_power_off);
asmlinkage void ret_from_fork(void); asmlinkage void ret_from_fork(void);
asmlinkage void ret_from_kernel_thread(void);
/* /*
* The idle loop on an H8/300.. * The idle loop on an H8/300..
...@@ -122,39 +123,6 @@ void show_regs(struct pt_regs * regs) ...@@ -122,39 +123,6 @@ void show_regs(struct pt_regs * regs)
printk("\n"); printk("\n");
} }
/*
* Create a kernel thread
*/
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
long retval;
long clone_arg;
mm_segment_t fs;
fs = get_fs();
set_fs (KERNEL_DS);
clone_arg = flags | CLONE_VM;
__asm__("mov.l sp,er3\n\t"
"sub.l er2,er2\n\t"
"mov.l %2,er1\n\t"
"mov.l %1,er0\n\t"
"trapa #0\n\t"
"cmp.l sp,er3\n\t"
"beq 1f\n\t"
"mov.l %4,er0\n\t"
"mov.l %3,er1\n\t"
"jsr @er1\n\t"
"mov.l %5,er0\n\t"
"trapa #0\n"
"1:\n\t"
"mov.l er0,%0"
:"=r"(retval)
:"i"(__NR_clone),"g"(clone_arg),"g"(fn),"g"(arg),"i"(__NR_exit)
:"er0","er1","er2","er3");
set_fs (fs);
return retval;
}
void flush_thread(void) void flush_thread(void)
{ {
} }
...@@ -198,6 +166,13 @@ int copy_thread(unsigned long clone_flags, ...@@ -198,6 +166,13 @@ int copy_thread(unsigned long clone_flags,
childregs = (struct pt_regs *) (THREAD_SIZE + task_stack_page(p)) - 1; childregs = (struct pt_regs *) (THREAD_SIZE + task_stack_page(p)) - 1;
if (unlikely(p->flags & PF_KTHREAD)) {
memset(childregs, 0, sizeof(struct pt_regs));
childregs->retpc = (unsigned long) ret_from_kernel_thread;
childregs->er4 = topstk; /* arg */
childregs->er5 = usp; /* fn */
p->thread.ksp = (unsigned long)childregs;
}
*childregs = *regs; *childregs = *regs;
childregs->retpc = (unsigned long) ret_from_fork; childregs->retpc = (unsigned long) ret_from_fork;
childregs->er0 = 0; childregs->er0 = 0;
...@@ -208,27 +183,6 @@ int copy_thread(unsigned long clone_flags, ...@@ -208,27 +183,6 @@ int copy_thread(unsigned long clone_flags,
return 0; return 0;
} }
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys_execve(const char *name,
const char *const *argv,
const char *const *envp,
int dummy, ...)
{
int error;
struct filename *filename;
struct pt_regs *regs = (struct pt_regs *) ((unsigned char *)&dummy-4);
filename = getname(name);
error = PTR_ERR(filename);
if (IS_ERR(filename))
return error;
error = do_execve(filename->name, argv, envp, regs);
putname(filename);
return error;
}
unsigned long thread_saved_pc(struct task_struct *tsk) unsigned long thread_saved_pc(struct task_struct *tsk)
{ {
return ((struct pt_regs *)tsk->thread.esp0)->pc; return ((struct pt_regs *)tsk->thread.esp0)->pc;
......
...@@ -46,29 +46,3 @@ asmlinkage void syscall_print(void *dummy,...) ...@@ -46,29 +46,3 @@ asmlinkage void syscall_print(void *dummy,...)
((regs->pc)&0xffffff)-2,regs->orig_er0,regs->er1,regs->er2,regs->er3,regs->er0); ((regs->pc)&0xffffff)-2,regs->orig_er0,regs->er1,regs->er2,regs->er3,regs->er0);
} }
#endif #endif
/*
* Do a system call from kernel instead of calling sys_execve so we
* end up with proper pt_regs.
*/
asmlinkage
int kernel_execve(const char *filename,
const char *const argv[],
const char *const envp[])
{
register long res __asm__("er0");
register const char *const *_c __asm__("er3") = envp;
register const char *const *_b __asm__("er2") = argv;
register const char * _a __asm__("er1") = filename;
__asm__ __volatile__ ("mov.l %1,er0\n\t"
"trapa #0\n\t"
: "=r" (res)
: "g" (__NR_execve),
"g" (_a),
"g" (_b),
"g" (_c)
: "cc", "memory");
return res;
}
...@@ -31,6 +31,8 @@ config HEXAGON ...@@ -31,6 +31,8 @@ config HEXAGON
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select GENERIC_CLOCKEVENTS_BROADCAST select GENERIC_CLOCKEVENTS_BROADCAST
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
---help--- ---help---
Qualcomm Hexagon is a processor architecture designed for high Qualcomm Hexagon is a processor architecture designed for high
performance and low power across a wide variety of applications. performance and low power across a wide variety of applications.
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
struct task_struct; struct task_struct;
/* this is defined in arch/process.c */ /* this is defined in arch/process.c */
extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
extern unsigned long thread_saved_pc(struct task_struct *tsk); extern unsigned long thread_saved_pc(struct task_struct *tsk);
extern void start_thread(struct pt_regs *, unsigned long, unsigned long); extern void start_thread(struct pt_regs *, unsigned long, unsigned long);
......
...@@ -32,4 +32,8 @@ ...@@ -32,4 +32,8 @@
extern int regs_query_register_offset(const char *name); extern int regs_query_register_offset(const char *name);
extern const char *regs_query_register_name(unsigned int offset); extern const char *regs_query_register_name(unsigned int offset);
#define current_pt_regs() \
((struct pt_regs *) \
((unsigned long)current_thread_info() + THREAD_SIZE) - 1)
#endif #endif
...@@ -27,5 +27,6 @@ ...@@ -27,5 +27,6 @@
*/ */
#define sys_mmap2 sys_mmap_pgoff #define sys_mmap2 sys_mmap_pgoff
#define __ARCH_WANT_SYS_EXECVE
#include <asm-generic/unistd.h> #include <asm-generic/unistd.h>
...@@ -25,33 +25,6 @@ ...@@ -25,33 +25,6 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/slab.h> #include <linux/slab.h>
/*
* Kernel thread creation. The desired kernel function is "wrapped"
* in the kernel_thread_helper function, which does cleanup
* afterwards.
*/
static void __noreturn kernel_thread_helper(void *arg, int (*fn)(void *))
{
do_exit(fn(arg));
}
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
/*
* Yes, we're exploting illicit knowledge of the ABI here.
*/
regs.r00 = (unsigned long) arg;
regs.r01 = (unsigned long) fn;
pt_set_elr(&regs, (unsigned long)kernel_thread_helper);
pt_set_kmode(&regs);
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}
EXPORT_SYMBOL(kernel_thread);
/* /*
* Program thread launch. Often defined as a macro in processor.h, * Program thread launch. Often defined as a macro in processor.h,
* but we're shooting for a small footprint and it's not an inner-loop * but we're shooting for a small footprint and it's not an inner-loop
...@@ -114,7 +87,7 @@ unsigned long thread_saved_pc(struct task_struct *tsk) ...@@ -114,7 +87,7 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
* Copy architecture-specific thread state * Copy architecture-specific thread state
*/ */
int copy_thread(unsigned long clone_flags, unsigned long usp, int copy_thread(unsigned long clone_flags, unsigned long usp,
unsigned long unused, struct task_struct *p, unsigned long arg, struct task_struct *p,
struct pt_regs *regs) struct pt_regs *regs)
{ {
struct thread_info *ti = task_thread_info(p); struct thread_info *ti = task_thread_info(p);
...@@ -125,19 +98,28 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -125,19 +98,28 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
childregs = (struct pt_regs *) (((unsigned long) ti + THREAD_SIZE) - childregs = (struct pt_regs *) (((unsigned long) ti + THREAD_SIZE) -
sizeof(*childregs)); sizeof(*childregs));
memcpy(childregs, regs, sizeof(*childregs));
ti->regs = childregs; ti->regs = childregs;
/* /*
* Establish kernel stack pointer and initial PC for new thread * Establish kernel stack pointer and initial PC for new thread
* Note that unlike the usual situation, we do not copy the
* parent's callee-saved here; those are in pt_regs and whatever
* we leave here will be overridden on return to userland.
*/ */
ss = (struct hexagon_switch_stack *) ((unsigned long) childregs - ss = (struct hexagon_switch_stack *) ((unsigned long) childregs -
sizeof(*ss)); sizeof(*ss));
ss->lr = (unsigned long)ret_from_fork; ss->lr = (unsigned long)ret_from_fork;
p->thread.switch_sp = ss; p->thread.switch_sp = ss;
if (unlikely(p->flags & PF_KTHREAD)) {
memset(childregs, 0, sizeof(struct pt_regs));
/* r24 <- fn, r25 <- arg */
ss->r2524 = usp | ((u64)arg << 32);
pt_set_kmode(childregs);
return 0;
}
memcpy(childregs, regs, sizeof(*childregs));
ss->r2524 = 0;
/* If User mode thread, set pt_reg stack pointer as per parameter */
if (user_mode(childregs)) {
pt_set_rte_sp(childregs, usp); pt_set_rte_sp(childregs, usp);
/* Child sees zero return value */ /* Child sees zero return value */
...@@ -160,26 +142,6 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -160,26 +142,6 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
* this point in the fork process * this point in the fork process
* Might also want to set things like ti->addr_limit * Might also want to set things like ti->addr_limit
*/ */
} else {
/*
* If kernel thread, resume stack is kernel stack base.
* Note that this is pointer arithmetic on pt_regs *
*/
pt_set_rte_sp(childregs, (unsigned long)(childregs + 1));
/*
* We need the current thread_info fast path pointer
* set up in pt_regs. The register to be used is
* parametric for assembler code, but the mechanism
* doesn't drop neatly into C. Needs to be fixed.
*/
childregs->THREADINFO_REG = (unsigned long) ti;
}
/*
* thread_info pointer is pulled out of task_struct "stack"
* field on switch_to.
*/
p->stack = (void *)ti;
return 0; return 0;
} }
......
...@@ -249,14 +249,14 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags) ...@@ -249,14 +249,14 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
*/ */
asmlinkage int sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) asmlinkage int sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss)
{ {
struct pt_regs *regs = current_thread_info()->regs; struct pt_regs *regs = current_pt_regs();
return do_sigaltstack(uss, uoss, regs->r29); return do_sigaltstack(uss, uoss, regs->r29);
} }
asmlinkage int sys_rt_sigreturn(void) asmlinkage int sys_rt_sigreturn(void)
{ {
struct pt_regs *regs = current_thread_info()->regs; struct pt_regs *regs = current_pt_regs();
struct rt_sigframe __user *frame; struct rt_sigframe __user *frame;
sigset_t blocked; sigset_t blocked;
......
...@@ -35,55 +35,13 @@ ...@@ -35,55 +35,13 @@
* See signal.c for signal-related system call wrappers. * See signal.c for signal-related system call wrappers.
*/ */
asmlinkage int sys_execve(char __user *ufilename,
const char __user *const __user *argv,
const char __user *const __user *envp)
{
struct pt_regs *pregs = current_thread_info()->regs;
struct filename *filename;
int retval;
filename = getname(ufilename);
retval = PTR_ERR(filename);
if (IS_ERR(filename))
return retval;
retval = do_execve(filename->name, argv, envp, pregs);
putname(filename);
return retval;
}
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
unsigned long parent_tidp, unsigned long child_tidp) unsigned long parent_tidp, unsigned long child_tidp)
{ {
struct pt_regs *pregs = current_thread_info()->regs; struct pt_regs *pregs = current_pt_regs();
if (!newsp) if (!newsp)
newsp = pregs->SP; newsp = pregs->SP;
return do_fork(clone_flags, newsp, pregs, 0, (int __user *)parent_tidp, return do_fork(clone_flags, newsp, pregs, 0, (int __user *)parent_tidp,
(int __user *)child_tidp); (int __user *)child_tidp);
} }
/*
* Do a system call from the kernel, so as to have a proper pt_regs
* and recycle the sys_execvpe infrustructure.
*/
int kernel_execve(const char *filename,
const char *const argv[], const char *const envp[])
{
register unsigned long __a0 asm("r0") = (unsigned long) filename;
register unsigned long __a1 asm("r1") = (unsigned long) argv;
register unsigned long __a2 asm("r2") = (unsigned long) envp;
int retval;
__asm__ volatile(
" R6 = #%4;\n"
" trap0(#1);\n"
" %0 = R0;\n"
: "=r" (retval)
: "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_execve)
);
return retval;
}
...@@ -266,4 +266,8 @@ _K_enter_machcheck: ...@@ -266,4 +266,8 @@ _K_enter_machcheck:
.globl ret_from_fork .globl ret_from_fork
ret_from_fork: ret_from_fork:
call schedule_tail call schedule_tail
P0 = cmp.eq(R24, #0);
if P0 jump return_from_syscall
R0 = R25;
callr R24
jump return_from_syscall jump return_from_syscall
...@@ -42,6 +42,8 @@ config IA64 ...@@ -42,6 +42,8 @@ config IA64
select GENERIC_TIME_VSYSCALL_OLD select GENERIC_TIME_VSYSCALL_OLD
select HAVE_MOD_ARCH_SPECIFIC select HAVE_MOD_ARCH_SPECIFIC
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
default y default y
help help
The Itanium Processor Family is Intel's 64-bit successor to The Itanium Processor Family is Intel's 64-bit successor to
......
...@@ -340,22 +340,6 @@ struct task_struct; ...@@ -340,22 +340,6 @@ struct task_struct;
*/ */
#define release_thread(dead_task) #define release_thread(dead_task)
/*
* This is the mechanism for creating a new kernel thread.
*
* NOTE 1: Only a kernel-only process (ie the swapper or direct
* descendants who haven't done an "execve()") should use this: it
* will work within a system call from a "real" process, but the
* process memory space will not be free'd until both the parent and
* the child have exited.
*
* NOTE 2: This MUST NOT be an inlined function. Otherwise, we get
* into trouble in init/main.c when the child thread returns to
* do_basic_setup() and the timing is such that free_initmem() has
* been called already.
*/
extern pid_t kernel_thread (int (*fn)(void *), void *arg, unsigned long flags);
/* Get wait channel for task P. */ /* Get wait channel for task P. */
extern unsigned long get_wchan (struct task_struct *p); extern unsigned long get_wchan (struct task_struct *p);
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_SYS_EXECVE
#if !defined(__ASSEMBLY__) && !defined(ASSEMBLER) #if !defined(__ASSEMBLY__) && !defined(ASSEMBLER)
......
...@@ -61,14 +61,13 @@ ENTRY(ia64_execve) ...@@ -61,14 +61,13 @@ ENTRY(ia64_execve)
* Allocate 8 input registers since ptrace() may clobber them * Allocate 8 input registers since ptrace() may clobber them
*/ */
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)
alloc loc1=ar.pfs,8,2,4,0 alloc loc1=ar.pfs,8,2,3,0
mov loc0=rp mov loc0=rp
.body .body
mov out0=in0 // filename mov out0=in0 // filename
;; // stop bit between alloc and call ;; // stop bit between alloc and call
mov out1=in1 // argv mov out1=in1 // argv
mov out2=in2 // envp mov out2=in2 // envp
add out3=16,sp // regs
br.call.sptk.many rp=sys_execve br.call.sptk.many rp=sys_execve
.ret0: .ret0:
cmp4.ge p6,p7=r8,r0 cmp4.ge p6,p7=r8,r0
...@@ -76,7 +75,6 @@ ENTRY(ia64_execve) ...@@ -76,7 +75,6 @@ ENTRY(ia64_execve)
sxt4 r8=r8 // return 64-bit result sxt4 r8=r8 // return 64-bit result
;; ;;
stf.spill [sp]=f0 stf.spill [sp]=f0
(p6) cmp.ne pKStk,pUStk=r0,r0 // a successful execve() lands us in user-mode...
mov rp=loc0 mov rp=loc0
(p6) mov ar.pfs=r0 // clear ar.pfs on success (p6) mov ar.pfs=r0 // clear ar.pfs on success
(p7) br.ret.sptk.many rp (p7) br.ret.sptk.many rp
...@@ -484,19 +482,6 @@ GLOBAL_ENTRY(prefetch_stack) ...@@ -484,19 +482,6 @@ GLOBAL_ENTRY(prefetch_stack)
br.ret.sptk.many rp br.ret.sptk.many rp
END(prefetch_stack) END(prefetch_stack)
GLOBAL_ENTRY(kernel_execve)
rum psr.ac
mov r15=__NR_execve // put syscall number in place
break __BREAK_SYSCALL
br.ret.sptk.many rp
END(kernel_execve)
GLOBAL_ENTRY(clone)
mov r15=__NR_clone // put syscall number in place
break __BREAK_SYSCALL
br.ret.sptk.many rp
END(clone)
/* /*
* Invoke a system call, but do some tracing before and after the call. * Invoke a system call, but do some tracing before and after the call.
* We MUST preserve the current register frame throughout this routine * We MUST preserve the current register frame throughout this routine
...@@ -600,6 +585,27 @@ GLOBAL_ENTRY(ia64_strace_leave_kernel) ...@@ -600,6 +585,27 @@ GLOBAL_ENTRY(ia64_strace_leave_kernel)
.ret4: br.cond.sptk ia64_leave_kernel .ret4: br.cond.sptk ia64_leave_kernel
END(ia64_strace_leave_kernel) END(ia64_strace_leave_kernel)
ENTRY(call_payload)
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(0)
/* call the kernel_thread payload; fn is in r4, arg - in r5 */
alloc loc1=ar.pfs,0,3,1,0
mov loc0=rp
mov loc2=gp
mov out0=r5 // arg
ld8 r14 = [r4], 8 // fn.address
;;
mov b6 = r14
ld8 gp = [r4] // fn.gp
;;
br.call.sptk.many rp=b6 // fn(arg)
.ret12: mov gp=loc2
mov rp=loc0
mov ar.pfs=loc1
/* ... and if it has returned, we are going to userland */
cmp.ne pKStk,pUStk=r0,r0
br.ret.sptk.many rp
END(call_payload)
GLOBAL_ENTRY(ia64_ret_from_clone) GLOBAL_ENTRY(ia64_ret_from_clone)
PT_REGS_UNWIND_INFO(0) PT_REGS_UNWIND_INFO(0)
{ /* { /*
...@@ -616,6 +622,7 @@ GLOBAL_ENTRY(ia64_ret_from_clone) ...@@ -616,6 +622,7 @@ GLOBAL_ENTRY(ia64_ret_from_clone)
br.call.sptk.many rp=ia64_invoke_schedule_tail br.call.sptk.many rp=ia64_invoke_schedule_tail
} }
.ret8: .ret8:
(pKStk) br.call.sptk.many rp=call_payload
adds r2=TI_FLAGS+IA64_TASK_SIZE,r13 adds r2=TI_FLAGS+IA64_TASK_SIZE,r13
;; ;;
ld4 r2=[r2] ld4 r2=[r2]
......
...@@ -1093,19 +1093,6 @@ GLOBAL_ENTRY(cycle_to_cputime) ...@@ -1093,19 +1093,6 @@ GLOBAL_ENTRY(cycle_to_cputime)
END(cycle_to_cputime) END(cycle_to_cputime)
#endif /* CONFIG_VIRT_CPU_ACCOUNTING */ #endif /* CONFIG_VIRT_CPU_ACCOUNTING */
GLOBAL_ENTRY(start_kernel_thread)
.prologue
.save rp, r0 // this is the end of the call-chain
.body
alloc r2 = ar.pfs, 0, 0, 2, 0
mov out0 = r9
mov out1 = r11;;
br.call.sptk.many rp = kernel_thread_helper;;
mov out0 = r8
br.call.sptk.many rp = sys_exit;;
1: br.sptk.few 1b // not reached
END(start_kernel_thread)
#ifdef CONFIG_IA64_BRL_EMU #ifdef CONFIG_IA64_BRL_EMU
/* /*
......
...@@ -401,64 +401,15 @@ copy_thread(unsigned long clone_flags, ...@@ -401,64 +401,15 @@ copy_thread(unsigned long clone_flags,
struct pt_regs *child_ptregs; struct pt_regs *child_ptregs;
int retval = 0; int retval = 0;
#ifdef CONFIG_SMP
/*
* For SMP idle threads, fork_by_hand() calls do_fork with
* NULL regs.
*/
if (!regs)
return 0;
#endif
stack = ((struct switch_stack *) regs) - 1;
child_ptregs = (struct pt_regs *) ((unsigned long) p + IA64_STK_OFFSET) - 1; child_ptregs = (struct pt_regs *) ((unsigned long) p + IA64_STK_OFFSET) - 1;
child_stack = (struct switch_stack *) child_ptregs - 1; child_stack = (struct switch_stack *) child_ptregs - 1;
/* copy parent's switch_stack & pt_regs to child: */
memcpy(child_stack, stack, sizeof(*child_ptregs) + sizeof(*child_stack));
rbs = (unsigned long) current + IA64_RBS_OFFSET; rbs = (unsigned long) current + IA64_RBS_OFFSET;
child_rbs = (unsigned long) p + IA64_RBS_OFFSET; child_rbs = (unsigned long) p + IA64_RBS_OFFSET;
rbs_size = stack->ar_bspstore - rbs;
/* copy the parent's register backing store to the child: */
memcpy((void *) child_rbs, (void *) rbs, rbs_size);
if (likely(user_mode(child_ptregs))) {
if (clone_flags & CLONE_SETTLS)
child_ptregs->r13 = regs->r16; /* see sys_clone2() in entry.S */
if (user_stack_base) {
child_ptregs->r12 = user_stack_base + user_stack_size - 16;
child_ptregs->ar_bspstore = user_stack_base;
child_ptregs->ar_rnat = 0;
child_ptregs->loadrs = 0;
}
} else {
/*
* Note: we simply preserve the relative position of
* the stack pointer here. There is no need to
* allocate a scratch area here, since that will have
* been taken care of by the caller of sys_clone()
* already.
*/
child_ptregs->r12 = (unsigned long) child_ptregs - 16; /* kernel sp */
child_ptregs->r13 = (unsigned long) p; /* set `current' pointer */
}
child_stack->ar_bspstore = child_rbs + rbs_size;
child_stack->b0 = (unsigned long) &ia64_ret_from_clone;
/* copy parts of thread_struct: */ /* copy parts of thread_struct: */
p->thread.ksp = (unsigned long) child_stack - 16; p->thread.ksp = (unsigned long) child_stack - 16;
/* stop some PSR bits from being inherited.
* the psr.up/psr.pp bits must be cleared on fork but inherited on execve()
* therefore we must specify them explicitly here and not include them in
* IA64_PSR_BITS_TO_CLEAR.
*/
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
/* /*
* NOTE: The calling convention considers all floating point * NOTE: The calling convention considers all floating point
* registers in the high partition (fph) to be scratch. Since * registers in the high partition (fph) to be scratch. Since
...@@ -480,8 +431,66 @@ copy_thread(unsigned long clone_flags, ...@@ -480,8 +431,66 @@ copy_thread(unsigned long clone_flags,
# define THREAD_FLAGS_TO_SET 0 # define THREAD_FLAGS_TO_SET 0
p->thread.flags = ((current->thread.flags & ~THREAD_FLAGS_TO_CLEAR) p->thread.flags = ((current->thread.flags & ~THREAD_FLAGS_TO_CLEAR)
| THREAD_FLAGS_TO_SET); | THREAD_FLAGS_TO_SET);
ia64_drop_fpu(p); /* don't pick up stale state from a CPU's fph */ ia64_drop_fpu(p); /* don't pick up stale state from a CPU's fph */
if (unlikely(p->flags & PF_KTHREAD)) {
if (unlikely(!user_stack_base)) {
/* fork_idle() called us */
return 0;
}
memset(child_stack, 0, sizeof(*child_ptregs) + sizeof(*child_stack));
child_stack->r4 = user_stack_base; /* payload */
child_stack->r5 = user_stack_size; /* argument */
/*
* Preserve PSR bits, except for bits 32-34 and 37-45,
* which we can't read.
*/
child_ptregs->cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN;
/* mark as valid, empty frame */
child_ptregs->cr_ifs = 1UL << 63;
child_stack->ar_fpsr = child_ptregs->ar_fpsr
= ia64_getreg(_IA64_REG_AR_FPSR);
child_stack->pr = (1 << PRED_KERNEL_STACK);
child_stack->ar_bspstore = child_rbs;
child_stack->b0 = (unsigned long) &ia64_ret_from_clone;
/* stop some PSR bits from being inherited.
* the psr.up/psr.pp bits must be cleared on fork but inherited on execve()
* therefore we must specify them explicitly here and not include them in
* IA64_PSR_BITS_TO_CLEAR.
*/
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
return 0;
}
stack = ((struct switch_stack *) regs) - 1;
/* copy parent's switch_stack & pt_regs to child: */
memcpy(child_stack, stack, sizeof(*child_ptregs) + sizeof(*child_stack));
/* copy the parent's register backing store to the child: */
rbs_size = stack->ar_bspstore - rbs;
memcpy((void *) child_rbs, (void *) rbs, rbs_size);
if (clone_flags & CLONE_SETTLS)
child_ptregs->r13 = regs->r16; /* see sys_clone2() in entry.S */
if (user_stack_base) {
child_ptregs->r12 = user_stack_base + user_stack_size - 16;
child_ptregs->ar_bspstore = user_stack_base;
child_ptregs->ar_rnat = 0;
child_ptregs->loadrs = 0;
}
child_stack->ar_bspstore = child_rbs + rbs_size;
child_stack->b0 = (unsigned long) &ia64_ret_from_clone;
/* stop some PSR bits from being inherited.
* the psr.up/psr.pp bits must be cleared on fork but inherited on execve()
* therefore we must specify them explicitly here and not include them in
* IA64_PSR_BITS_TO_CLEAR.
*/
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
#ifdef CONFIG_PERFMON #ifdef CONFIG_PERFMON
if (current->thread.pfm_context) if (current->thread.pfm_context)
pfm_inherit(p, child_ptregs); pfm_inherit(p, child_ptregs);
...@@ -608,57 +617,6 @@ dump_fpu (struct pt_regs *pt, elf_fpregset_t dst) ...@@ -608,57 +617,6 @@ dump_fpu (struct pt_regs *pt, elf_fpregset_t dst)
return 1; /* f0-f31 are always valid so we always return 1 */ return 1; /* f0-f31 are always valid so we always return 1 */
} }
long
sys_execve (const char __user *filename,
const char __user *const __user *argv,
const char __user *const __user *envp,
struct pt_regs *regs)
{
struct filename *fname;
int error;
fname = getname(filename);
error = PTR_ERR(fname);
if (IS_ERR(fname))
goto out;
error = do_execve(fname->name, argv, envp, regs);
putname(fname);
out:
return error;
}
pid_t
kernel_thread (int (*fn)(void *), void *arg, unsigned long flags)
{
extern void start_kernel_thread (void);
unsigned long *helper_fptr = (unsigned long *) &start_kernel_thread;
struct {
struct switch_stack sw;
struct pt_regs pt;
} regs;
memset(&regs, 0, sizeof(regs));
regs.pt.cr_iip = helper_fptr[0]; /* set entry point (IP) */
regs.pt.r1 = helper_fptr[1]; /* set GP */
regs.pt.r9 = (unsigned long) fn; /* 1st argument */
regs.pt.r11 = (unsigned long) arg; /* 2nd argument */
/* Preserve PSR bits, except for bits 32-34 and 37-45, which we can't read. */
regs.pt.cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN;
regs.pt.cr_ifs = 1UL << 63; /* mark as valid, empty frame */
regs.sw.ar_fpsr = regs.pt.ar_fpsr = ia64_getreg(_IA64_REG_AR_FPSR);
regs.sw.ar_bspstore = (unsigned long) current + IA64_RBS_OFFSET;
regs.sw.pr = (1 << PRED_KERNEL_STACK);
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs.pt, 0, NULL, NULL);
}
EXPORT_SYMBOL(kernel_thread);
/* This gets called from kernel_thread() via ia64_invoke_thread_helper(). */
int
kernel_thread_helper (int (*fn)(void *), void *arg)
{
return (*fn)(arg);
}
/* /*
* Flush thread state. This is called when a thread does an execve(). * Flush thread state. This is called when a thread does an execve().
*/ */
......
...@@ -15,6 +15,8 @@ config M32R ...@@ -15,6 +15,8 @@ config M32R
select GENERIC_ATOMIC64 select GENERIC_ATOMIC64
select ARCH_USES_GETTIMEOFFSET select ARCH_USES_GETTIMEOFFSET
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
config SBUS config SBUS
bool bool
......
...@@ -118,11 +118,6 @@ struct mm_struct; ...@@ -118,11 +118,6 @@ struct mm_struct;
/* Free all resources held by a thread. */ /* Free all resources held by a thread. */
extern void release_thread(struct task_struct *); extern void release_thread(struct task_struct *);
/*
* create a kernel thread without removing it from tasklists
*/
extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
/* Copy and release all segment info associated with a VM */ /* Copy and release all segment info associated with a VM */
extern void copy_segments(struct task_struct *p, struct mm_struct * mm); extern void copy_segments(struct task_struct *p, struct mm_struct * mm);
extern void release_segments(struct mm_struct * mm); extern void release_segments(struct mm_struct * mm);
......
...@@ -139,6 +139,8 @@ extern void withdraw_debug_trap(struct pt_regs *regs); ...@@ -139,6 +139,8 @@ extern void withdraw_debug_trap(struct pt_regs *regs);
#define task_pt_regs(task) \ #define task_pt_regs(task) \
((struct pt_regs *)(task_stack_page(task) + THREAD_SIZE) - 1) ((struct pt_regs *)(task_stack_page(task) + THREAD_SIZE) - 1)
#define current_pt_regs() ((struct pt_regs *) \
((unsigned long)current_thread_info() + THREAD_SIZE) - 1)
#endif /* __KERNEL */ #endif /* __KERNEL */
......
...@@ -352,6 +352,7 @@ ...@@ -352,6 +352,7 @@
#define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_OLDUMOUNT
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_SYS_EXECVE
#define __IGNORE_lchown #define __IGNORE_lchown
#define __IGNORE_setuid #define __IGNORE_setuid
......
...@@ -125,6 +125,15 @@ ...@@ -125,6 +125,15 @@
and \reg, sp and \reg, sp
.endm .endm
ENTRY(ret_from_kernel_thread)
pop r0
bl schedule_tail
GET_THREAD_INFO(r8)
ld r0, R0(r8)
ld r1, R1(r8)
jl r1
bra syscall_exit
ENTRY(ret_from_fork) ENTRY(ret_from_fork)
pop r0 pop r0
bl schedule_tail bl schedule_tail
......
...@@ -164,41 +164,6 @@ void show_regs(struct pt_regs * regs) ...@@ -164,41 +164,6 @@ void show_regs(struct pt_regs * regs)
#endif #endif
} }
/*
* Create a kernel thread
*/
/*
* This is the mechanism for creating a new kernel thread.
*
* NOTE! Only a kernel-only process(ie the swapper or direct descendants
* who haven't done an "execve()") should use this: it will work within
* a system call from a "real" process, but the process memory space will
* not be free'd until both the parent and the child have exited.
*/
static void kernel_thread_helper(void *nouse, int (*fn)(void *), void *arg)
{
fn(arg);
do_exit(-1);
}
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof (regs));
regs.r1 = (unsigned long)fn;
regs.r2 = (unsigned long)arg;
regs.bpc = (unsigned long)kernel_thread_helper;
regs.psw = M32R_PSW_BIE;
/* Ok, create the new process. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL,
NULL);
}
/* /*
* Free current thread data structures etc.. * Free current thread data structures etc..
*/ */
...@@ -227,29 +192,35 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) ...@@ -227,29 +192,35 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
} }
int copy_thread(unsigned long clone_flags, unsigned long spu, int copy_thread(unsigned long clone_flags, unsigned long spu,
unsigned long unused, struct task_struct *tsk, struct pt_regs *regs) unsigned long arg, struct task_struct *tsk, struct pt_regs *regs)
{ {
struct pt_regs *childregs = task_pt_regs(tsk); struct pt_regs *childregs = task_pt_regs(tsk);
extern void ret_from_fork(void); extern void ret_from_fork(void);
extern void ret_from_kernel_thread(void);
if (unlikely(tsk->flags & PF_KTHREAD)) {
memset(childregs, 0, sizeof(struct pt_regs));
childregs->psw = M32R_PSW_BIE;
childregs->r1 = spu; /* fn */
childregs->r0 = arg;
tsk->thread.lr = (unsigned long)ret_from_kernel_thread;
} else {
/* Copy registers */ /* Copy registers */
*childregs = *regs; *childregs = *regs;
childregs->spu = spu; childregs->spu = spu;
childregs->r0 = 0; /* Child gets zero as return value */ childregs->r0 = 0; /* Child gets zero as return value */
regs->r0 = tsk->pid;
tsk->thread.sp = (unsigned long)childregs;
tsk->thread.lr = (unsigned long)ret_from_fork; tsk->thread.lr = (unsigned long)ret_from_fork;
}
tsk->thread.sp = (unsigned long)childregs;
return 0; return 0;
} }
asmlinkage int sys_fork(unsigned long r0, unsigned long r1, unsigned long r2, asmlinkage int sys_fork(void)
unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6,
struct pt_regs regs)
{ {
#ifdef CONFIG_MMU #ifdef CONFIG_MMU
return do_fork(SIGCHLD, regs.spu, &regs, 0, NULL, NULL); struct pt_regs *regs = current_pt_regs();
return do_fork(SIGCHLD, regs->spu, regs, 0, NULL, NULL);
#else #else
return -EINVAL; return -EINVAL;
#endif /* CONFIG_MMU */ #endif /* CONFIG_MMU */
...@@ -257,14 +228,13 @@ asmlinkage int sys_fork(unsigned long r0, unsigned long r1, unsigned long r2, ...@@ -257,14 +228,13 @@ asmlinkage int sys_fork(unsigned long r0, unsigned long r1, unsigned long r2,
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
unsigned long parent_tidptr, unsigned long parent_tidptr,
unsigned long child_tidptr, unsigned long child_tidptr)
unsigned long r4, unsigned long r5, unsigned long r6,
struct pt_regs regs)
{ {
struct pt_regs *regs = current_pt_regs();
if (!newsp) if (!newsp)
newsp = regs.spu; newsp = regs->spu;
return do_fork(clone_flags, newsp, &regs, 0, return do_fork(clone_flags, newsp, regs, 0,
(int __user *)parent_tidptr, (int __user *)child_tidptr); (int __user *)parent_tidptr, (int __user *)child_tidptr);
} }
...@@ -278,37 +248,13 @@ asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, ...@@ -278,37 +248,13 @@ asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
* do not have enough call-clobbered registers to hold all * do not have enough call-clobbered registers to hold all
* the information you need. * the information you need.
*/ */
asmlinkage int sys_vfork(unsigned long r0, unsigned long r1, unsigned long r2, asmlinkage int sys_vfork(void)
unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6,
struct pt_regs regs)
{ {
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.spu, &regs, 0, struct pt_regs *regs = current_pt_regs();
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->spu, regs, 0,
NULL, NULL); NULL, NULL);
} }
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys_execve(const char __user *ufilename,
const char __user *const __user *uargv,
const char __user *const __user *uenvp,
unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, struct pt_regs regs)
{
int error;
struct filename *filename;
filename = getname(ufilename);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename->name, uargv, uenvp, &regs);
putname(filename);
out:
return error;
}
/* /*
* These bracket the sleeping functions.. * These bracket the sleeping functions..
*/ */
......
...@@ -88,24 +88,3 @@ asmlinkage int sys_cachectl(char *addr, int nbytes, int op) ...@@ -88,24 +88,3 @@ asmlinkage int sys_cachectl(char *addr, int nbytes, int op)
/* Not implemented yet. */ /* Not implemented yet. */
return -ENOSYS; return -ENOSYS;
} }
/*
* Do a system call from kernel instead of calling sys_execve so we
* end up with proper pt_regs.
*/
int kernel_execve(const char *filename,
const char *const argv[],
const char *const envp[])
{
register long __scno __asm__ ("r7") = __NR_execve;
register long __arg3 __asm__ ("r2") = (long)(envp);
register long __arg2 __asm__ ("r1") = (long)(argv);
register long __res __asm__ ("r0") = (long)(filename);
__asm__ __volatile__ (
"trap #" SYSCALL_VECTOR "|| nop"
: "=r" (__res)
: "r" (__scno), "0" (__res), "r" (__arg2),
"r" (__arg3)
: "memory");
return __res;
}
...@@ -16,6 +16,7 @@ config M68K ...@@ -16,6 +16,7 @@ config M68K
select ARCH_WANT_IPC_PARSE_VERSION select ARCH_WANT_IPC_PARSE_VERSION
select ARCH_USES_GETTIMEOFFSET if MMU && !COLDFIRE select ARCH_USES_GETTIMEOFFSET if MMU && !COLDFIRE
select GENERIC_KERNEL_THREAD select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
select HAVE_MOD_ARCH_SPECIFIC select HAVE_MOD_ARCH_SPECIFIC
select MODULES_USE_ELF_REL select MODULES_USE_ELF_REL
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
......
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_SYS_EXECVE #define __ARCH_WANT_SYS_EXECVE
#define __ARCH_WANT_KERNEL_EXECVE
/* /*
* "Conditional" syscalls * "Conditional" syscalls
......
...@@ -115,16 +115,9 @@ ENTRY(ret_from_kernel_thread) ...@@ -115,16 +115,9 @@ ENTRY(ret_from_kernel_thread)
| a3 contains the kernel thread payload, d7 - its argument | a3 contains the kernel thread payload, d7 - its argument
movel %d1,%sp@- movel %d1,%sp@-
jsr schedule_tail jsr schedule_tail
GET_CURRENT(%d0)
movel %d7,(%sp) movel %d7,(%sp)
jsr %a3@ jsr %a3@
addql #4,%sp addql #4,%sp
movel %d0,(%sp)
jra sys_exit
ENTRY(ret_from_kernel_execve)
movel 4(%sp), %sp
GET_CURRENT(%d0)
jra ret_from_exception jra ret_from_exception
#if defined(CONFIG_COLDFIRE) || !defined(CONFIG_MMU) #if defined(CONFIG_COLDFIRE) || !defined(CONFIG_MMU)
......
...@@ -26,6 +26,8 @@ config MICROBLAZE ...@@ -26,6 +26,8 @@ config MICROBLAZE
select GENERIC_ATOMIC64 select GENERIC_ATOMIC64
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
config SWAP config SWAP
def_bool n def_bool n
......
...@@ -31,6 +31,7 @@ extern const struct seq_operations cpuinfo_op; ...@@ -31,6 +31,7 @@ extern const struct seq_operations cpuinfo_op;
void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp); void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp);
extern void ret_from_fork(void); extern void ret_from_fork(void);
extern void ret_from_kernel_thread(void);
# endif /* __ASSEMBLY__ */ # endif /* __ASSEMBLY__ */
...@@ -78,11 +79,6 @@ extern unsigned long thread_saved_pc(struct task_struct *t); ...@@ -78,11 +79,6 @@ extern unsigned long thread_saved_pc(struct task_struct *t);
extern unsigned long get_wchan(struct task_struct *p); extern unsigned long get_wchan(struct task_struct *p);
/*
* create a kernel thread without removing it from tasklists
*/
extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
# define KSTK_EIP(tsk) (0) # define KSTK_EIP(tsk) (0)
# define KSTK_ESP(tsk) (0) # define KSTK_ESP(tsk) (0)
...@@ -131,8 +127,6 @@ extern inline void release_thread(struct task_struct *dead_task) ...@@ -131,8 +127,6 @@ extern inline void release_thread(struct task_struct *dead_task)
{ {
} }
extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
/* Free current thread data structures etc. */ /* Free current thread data structures etc. */
static inline void exit_thread(void) static inline void exit_thread(void)
{ {
......
...@@ -422,6 +422,7 @@ ...@@ -422,6 +422,7 @@
#define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_SIGPROCMASK
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_SYS_EXECVE
/* /*
* "Conditional" syscalls * "Conditional" syscalls
......
...@@ -474,6 +474,14 @@ ENTRY(ret_from_fork) ...@@ -474,6 +474,14 @@ ENTRY(ret_from_fork)
brid ret_to_user brid ret_to_user
nop nop
ENTRY(ret_from_kernel_thread)
brlid r15, schedule_tail
addk r5, r0, r3
brald r15, r20
addk r5, r0, r19
brid ret_to_user
addk r3, r0, r0
work_pending: work_pending:
enable_irq enable_irq
...@@ -559,10 +567,6 @@ sys_clone: ...@@ -559,10 +567,6 @@ sys_clone:
brid microblaze_clone brid microblaze_clone
addk r7, r1, r0 addk r7, r1, r0
sys_execve:
brid microblaze_execve
addk r8, r1, r0
sys_rt_sigreturn_wrapper: sys_rt_sigreturn_wrapper:
brid sys_rt_sigreturn brid sys_rt_sigreturn
addk r5, r1, r0 addk r5, r1, r0
......
...@@ -293,24 +293,6 @@ C_ENTRY(_user_exception): ...@@ -293,24 +293,6 @@ C_ENTRY(_user_exception):
swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */
addi r14, r14, 4 /* return address is 4 byte after call */ addi r14, r14, 4 /* return address is 4 byte after call */
mfs r1, rmsr
nop
andi r1, r1, MSR_UMS
bnei r1, 1f
/* Kernel-mode state save - kernel execve */
lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/
tophys(r1,r1);
addik r1, r1, -PT_SIZE; /* Make room on the stack. */
SAVE_REGS
swi r1, r1, PT_MODE; /* pt_regs -> kernel mode */
brid 2f;
nop; /* Fill delay slot */
/* User-mode state save. */
1:
lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
tophys(r1,r1); tophys(r1,r1);
lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct */ lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct */
...@@ -479,11 +461,20 @@ C_ENTRY(sys_fork_wrapper): ...@@ -479,11 +461,20 @@ C_ENTRY(sys_fork_wrapper):
saved context). */ saved context). */
C_ENTRY(ret_from_fork): C_ENTRY(ret_from_fork):
bralid r15, schedule_tail; /* ...which is schedule_tail's arg */ bralid r15, schedule_tail; /* ...which is schedule_tail's arg */
add r3, r5, r0; /* switch_thread returns the prev task */ add r5, r3, r0; /* switch_thread returns the prev task */
/* ( in the delay slot ) */ /* ( in the delay slot ) */
brid ret_from_trap; /* Do normal trap return */ brid ret_from_trap; /* Do normal trap return */
add r3, r0, r0; /* Child's fork call should return 0. */ add r3, r0, r0; /* Child's fork call should return 0. */
C_ENTRY(ret_from_kernel_thread):
bralid r15, schedule_tail; /* ...which is schedule_tail's arg */
add r5, r3, r0; /* switch_thread returns the prev task */
/* ( in the delay slot ) */
brald r15, r20 /* fn was left in r20 */
addk r5, r0, r19 /* ... and argument - in r19 */
brid ret_from_trap
add r3, r0, r0
C_ENTRY(sys_vfork): C_ENTRY(sys_vfork):
brid microblaze_vfork /* Do real work (tail-call) */ brid microblaze_vfork /* Do real work (tail-call) */
addik r5, r1, 0 addik r5, r1, 0
...@@ -498,10 +489,6 @@ C_ENTRY(sys_clone): ...@@ -498,10 +489,6 @@ C_ENTRY(sys_clone):
brid do_fork /* Do real work (tail-call) */ brid do_fork /* Do real work (tail-call) */
add r8, r0, r0; /* Arg 3: (unused) */ add r8, r0, r0; /* Arg 3: (unused) */
C_ENTRY(sys_execve):
brid microblaze_execve; /* Do real work (tail-call).*/
addik r8, r1, 0; /* add user context as 4th arg */
C_ENTRY(sys_rt_sigreturn_wrapper): C_ENTRY(sys_rt_sigreturn_wrapper):
brid sys_rt_sigreturn /* Do real work */ brid sys_rt_sigreturn /* Do real work */
addik r5, r1, 0; /* add user context as 1st arg */ addik r5, r1, 0; /* add user context as 1st arg */
......
...@@ -119,46 +119,38 @@ void flush_thread(void) ...@@ -119,46 +119,38 @@ void flush_thread(void)
} }
int copy_thread(unsigned long clone_flags, unsigned long usp, int copy_thread(unsigned long clone_flags, unsigned long usp,
unsigned long unused, unsigned long arg,
struct task_struct *p, struct pt_regs *regs) struct task_struct *p, struct pt_regs *regs)
{ {
struct pt_regs *childregs = task_pt_regs(p); struct pt_regs *childregs = task_pt_regs(p);
struct thread_info *ti = task_thread_info(p); struct thread_info *ti = task_thread_info(p);
if (unlikely(p->flags & PF_KTHREAD)) {
/* if we're creating a new kernel thread then just zeroing all
* the registers. That's OK for a brand new thread.*/
memset(childregs, 0, sizeof(struct pt_regs));
memset(&ti->cpu_context, 0, sizeof(struct cpu_context));
ti->cpu_context.r1 = (unsigned long)childregs;
ti->cpu_context.r20 = (unsigned long)usp; /* fn */
ti->cpu_context.r19 = (unsigned long)arg;
childregs->pt_mode = 1;
local_save_flags(childregs->msr);
#ifdef CONFIG_MMU
ti->cpu_context.msr = childregs->msr & ~MSR_IE;
#endif
ti->cpu_context.r15 = (unsigned long)ret_from_kernel_thread - 8;
return 0;
}
*childregs = *regs; *childregs = *regs;
if (user_mode(regs))
childregs->r1 = usp; childregs->r1 = usp;
else
childregs->r1 = ((unsigned long) ti) + THREAD_SIZE;
#ifndef CONFIG_MMU
memset(&ti->cpu_context, 0, sizeof(struct cpu_context)); memset(&ti->cpu_context, 0, sizeof(struct cpu_context));
ti->cpu_context.r1 = (unsigned long)childregs; ti->cpu_context.r1 = (unsigned long)childregs;
#ifndef CONFIG_MMU
ti->cpu_context.msr = (unsigned long)childregs->msr; ti->cpu_context.msr = (unsigned long)childregs->msr;
#else #else
/* if creating a kernel thread then update the current reg (we don't
* want to use the parent's value when restoring by POP_STATE) */
if (kernel_mode(regs))
/* save new current on stack to use POP_STATE */
childregs->CURRENT_TASK = (unsigned long)p;
/* if returning to user then use the parent's value of this register */
/* if we're creating a new kernel thread then just zeroing all
* the registers. That's OK for a brand new thread.*/
/* Pls. note that some of them will be restored in POP_STATE */
if (kernel_mode(regs))
memset(&ti->cpu_context, 0, sizeof(struct cpu_context));
/* if this thread is created for fork/vfork/clone, then we want to
* restore all the parent's context */
/* in addition to the registers which will be restored by POP_STATE */
else {
ti->cpu_context = *(struct cpu_context *)regs;
childregs->msr |= MSR_UMS; childregs->msr |= MSR_UMS;
}
/* FIXME STATE_SAVE_PT_OFFSET; */
ti->cpu_context.r1 = (unsigned long)childregs;
/* we should consider the fact that childregs is a copy of the parent /* we should consider the fact that childregs is a copy of the parent
* regs which were saved immediately after entering the kernel state * regs which were saved immediately after entering the kernel state
* before enabling VM. This MSR will be restored in switch_to and * before enabling VM. This MSR will be restored in switch_to and
...@@ -209,29 +201,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk) ...@@ -209,29 +201,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
} }
#endif #endif
static void kernel_thread_helper(int (*fn)(void *), void *arg)
{
fn(arg);
do_exit(-1);
}
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
/* store them in non-volatile registers */
regs.r5 = (unsigned long)fn;
regs.r6 = (unsigned long)arg;
local_save_flags(regs.msr);
regs.pc = (unsigned long)kernel_thread_helper;
regs.pt_mode = 1;
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0,
&regs, 0, NULL, NULL);
}
EXPORT_SYMBOL_GPL(kernel_thread);
unsigned long get_wchan(struct task_struct *p) unsigned long get_wchan(struct task_struct *p)
{ {
/* TBD (used by procfs) */ /* TBD (used by procfs) */
...@@ -246,6 +215,7 @@ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp) ...@@ -246,6 +215,7 @@ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp)
regs->pt_mode = 0; regs->pt_mode = 0;
#ifdef CONFIG_MMU #ifdef CONFIG_MMU
regs->msr |= MSR_UMS; regs->msr |= MSR_UMS;
regs->msr &= ~MSR_VM;
#endif #endif
} }
......
...@@ -48,24 +48,6 @@ asmlinkage long microblaze_clone(int flags, unsigned long stack, ...@@ -48,24 +48,6 @@ asmlinkage long microblaze_clone(int flags, unsigned long stack,
return do_fork(flags, stack, regs, 0, NULL, NULL); return do_fork(flags, stack, regs, 0, NULL, NULL);
} }
asmlinkage long microblaze_execve(const char __user *filenamei,
const char __user *const __user *argv,
const char __user *const __user *envp,
struct pt_regs *regs)
{
int error;
struct filename *filename;
filename = getname(filenamei);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename->name, argv, envp, regs);
putname(filename);
out:
return error;
}
asmlinkage long sys_mmap(unsigned long addr, unsigned long len, asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long prot, unsigned long flags,
unsigned long fd, off_t pgoff) unsigned long fd, off_t pgoff)
...@@ -75,24 +57,3 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len, ...@@ -75,24 +57,3 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff >> PAGE_SHIFT); return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff >> PAGE_SHIFT);
} }
/*
* Do a system call from kernel instead of calling sys_execve so we
* end up with proper pt_regs.
*/
int kernel_execve(const char *filename,
const char *const argv[],
const char *const envp[])
{
register const char *__a __asm__("r5") = filename;
register const void *__b __asm__("r6") = argv;
register const void *__c __asm__("r7") = envp;
register unsigned long __syscall __asm__("r12") = __NR_execve;
register unsigned long __ret __asm__("r3");
__asm__ __volatile__ ("brki r14, 0x8"
: "=r" (__ret), "=r" (__syscall)
: "1" (__syscall), "r" (__a), "r" (__b), "r" (__c)
: "r4", "r8", "r9",
"r10", "r11", "r14", "cc", "memory");
return __ret;
}
...@@ -40,6 +40,8 @@ config MIPS ...@@ -40,6 +40,8 @@ config MIPS
select HAVE_MOD_ARCH_SPECIFIC select HAVE_MOD_ARCH_SPECIFIC
select MODULES_USE_ELF_REL select MODULES_USE_ELF_REL
select MODULES_USE_ELF_RELA if 64BIT select MODULES_USE_ELF_RELA if 64BIT
select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
menu "Machine selection" menu "Machine selection"
......
...@@ -310,8 +310,6 @@ struct task_struct; ...@@ -310,8 +310,6 @@ struct task_struct;
/* Free all resources held by a thread. */ /* Free all resources held by a thread. */
#define release_thread(thread) do { } while(0) #define release_thread(thread) do { } while(0)
extern long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
extern unsigned long thread_saved_pc(struct task_struct *tsk); extern unsigned long thread_saved_pc(struct task_struct *tsk);
/* /*
......
...@@ -61,4 +61,10 @@ static inline void die_if_kernel(const char *str, struct pt_regs *regs) ...@@ -61,4 +61,10 @@ static inline void die_if_kernel(const char *str, struct pt_regs *regs)
die(str, regs); die(str, regs);
} }
#define current_pt_regs() \
({ \
unsigned long sp = (unsigned long)__builtin_frame_address(0); \
(struct pt_regs *)((sp | (THREAD_SIZE - 1)) + 1 - 32) - 1; \
})
#endif /* _ASM_PTRACE_H */ #endif /* _ASM_PTRACE_H */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#define __ARCH_OMIT_COMPAT_SYS_GETDENTS64 #define __ARCH_OMIT_COMPAT_SYS_GETDENTS64
#define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_READDIR
#define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_ALARM
#define __ARCH_WANT_SYS_EXECVE
#define __ARCH_WANT_SYS_GETHOSTNAME #define __ARCH_WANT_SYS_GETHOSTNAME
#define __ARCH_WANT_SYS_IPC #define __ARCH_WANT_SYS_IPC
#define __ARCH_WANT_SYS_PAUSE #define __ARCH_WANT_SYS_PAUSE
......
...@@ -65,6 +65,12 @@ need_resched: ...@@ -65,6 +65,12 @@ need_resched:
b need_resched b need_resched
#endif #endif
FEXPORT(ret_from_kernel_thread)
jal schedule_tail # a0 = struct task_struct *prev
move a0, s1
jal s0
j syscall_exit
FEXPORT(ret_from_fork) FEXPORT(ret_from_fork)
jal schedule_tail # a0 = struct task_struct *prev jal schedule_tail # a0 = struct task_struct *prev
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
* *
* Copyright (C) 2000 Silicon Graphics, Inc. * Copyright (C) 2000 Silicon Graphics, Inc.
* Written by Ulf Carlsson (ulfc@engr.sgi.com) * Written by Ulf Carlsson (ulfc@engr.sgi.com)
* sys32_execve from ia64/ia32 code, Feb 2000, Kanoj Sarcar (kanoj@sgi.com)
*/ */
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/mm.h> #include <linux/mm.h>
...@@ -77,26 +76,6 @@ SYSCALL_DEFINE6(32_mmap2, unsigned long, addr, unsigned long, len, ...@@ -77,26 +76,6 @@ SYSCALL_DEFINE6(32_mmap2, unsigned long, addr, unsigned long, len,
return error; return error;
} }
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys32_execve(nabi_no_regargs struct pt_regs regs)
{
int error;
struct filename *filename;
filename = getname(compat_ptr(regs.regs[4]));
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = compat_do_execve(filename->name, compat_ptr(regs.regs[5]),
compat_ptr(regs.regs[6]), &regs);
putname(filename);
out:
return error;
}
#define RLIM_INFINITY32 0x7fffffff #define RLIM_INFINITY32 0x7fffffff
#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) #define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x)
......
...@@ -32,8 +32,6 @@ EXPORT_SYMBOL(memset); ...@@ -32,8 +32,6 @@ EXPORT_SYMBOL(memset);
EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(memmove);
EXPORT_SYMBOL(kernel_thread);
/* /*
* Functions that operate on entire pages. Mostly used by memory management. * Functions that operate on entire pages. Mostly used by memory management.
*/ */
......
...@@ -84,6 +84,7 @@ void __noreturn cpu_idle(void) ...@@ -84,6 +84,7 @@ void __noreturn cpu_idle(void)
} }
asmlinkage void ret_from_fork(void); asmlinkage void ret_from_fork(void);
asmlinkage void ret_from_kernel_thread(void);
void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
{ {
...@@ -113,7 +114,7 @@ void flush_thread(void) ...@@ -113,7 +114,7 @@ void flush_thread(void)
} }
int copy_thread(unsigned long clone_flags, unsigned long usp, 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 *regs)
{ {
struct thread_info *ti = task_thread_info(p); struct thread_info *ti = task_thread_info(p);
struct pt_regs *childregs; struct pt_regs *childregs;
...@@ -136,19 +137,30 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -136,19 +137,30 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
childregs = (struct pt_regs *) childksp - 1; childregs = (struct pt_regs *) childksp - 1;
/* Put the stack after the struct pt_regs. */ /* Put the stack after the struct pt_regs. */
childksp = (unsigned long) childregs; childksp = (unsigned long) childregs;
p->thread.cp0_status = read_c0_status() & ~(ST0_CU2|ST0_CU1);
if (unlikely(p->flags & PF_KTHREAD)) {
unsigned long status = p->thread.cp0_status;
memset(childregs, 0, sizeof(struct pt_regs));
ti->addr_limit = KERNEL_DS;
p->thread.reg16 = usp; /* fn */
p->thread.reg17 = arg;
p->thread.reg29 = childksp;
p->thread.reg31 = (unsigned long) ret_from_kernel_thread;
#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
status = (status & ~(ST0_KUP | ST0_IEP | ST0_IEC)) |
((status & (ST0_KUC | ST0_IEC)) << 2);
#else
status |= ST0_EXL;
#endif
childregs->cp0_status = status;
return 0;
}
*childregs = *regs; *childregs = *regs;
childregs->regs[7] = 0; /* Clear error flag */ childregs->regs[7] = 0; /* Clear error flag */
childregs->regs[2] = 0; /* Child gets zero as return value */ childregs->regs[2] = 0; /* Child gets zero as return value */
if (childregs->cp0_status & ST0_CU0) {
childregs->regs[28] = (unsigned long) ti;
childregs->regs[29] = childksp;
ti->addr_limit = KERNEL_DS;
} else {
childregs->regs[29] = usp; childregs->regs[29] = usp;
ti->addr_limit = USER_DS; ti->addr_limit = USER_DS;
}
p->thread.reg29 = (unsigned long) childregs; p->thread.reg29 = (unsigned long) childregs;
p->thread.reg31 = (unsigned long) ret_from_fork; p->thread.reg31 = (unsigned long) ret_from_fork;
...@@ -156,7 +168,6 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -156,7 +168,6 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
* New tasks lose permission to use the fpu. This accelerates context * New tasks lose permission to use the fpu. This accelerates context
* switching for most programs since they don't use the fpu. * switching for most programs since they don't use the fpu.
*/ */
p->thread.cp0_status = read_c0_status() & ~(ST0_CU2|ST0_CU1);
childregs->cp0_status &= ~(ST0_CU2|ST0_CU1); childregs->cp0_status &= ~(ST0_CU2|ST0_CU1);
#ifdef CONFIG_MIPS_MT_SMTC #ifdef CONFIG_MIPS_MT_SMTC
...@@ -221,35 +232,6 @@ int dump_task_fpu(struct task_struct *t, elf_fpregset_t *fpr) ...@@ -221,35 +232,6 @@ int dump_task_fpu(struct task_struct *t, elf_fpregset_t *fpr)
return 1; return 1;
} }
/*
* Create a kernel thread
*/
static void __noreturn kernel_thread_helper(void *arg, int (*fn)(void *))
{
do_exit(fn(arg));
}
long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
regs.regs[4] = (unsigned long) arg;
regs.regs[5] = (unsigned long) fn;
regs.cp0_epc = (unsigned long) kernel_thread_helper;
regs.cp0_status = read_c0_status();
#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
regs.cp0_status = (regs.cp0_status & ~(ST0_KUP | ST0_IEP | ST0_IEC)) |
((regs.cp0_status & (ST0_KUC | ST0_IEC)) << 2);
#else
regs.cp0_status |= ST0_EXL;
#endif
/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}
/* /*
* *
*/ */
......
...@@ -167,7 +167,7 @@ EXPORT(sysn32_call_table) ...@@ -167,7 +167,7 @@ EXPORT(sysn32_call_table)
PTR sys_getsockopt PTR sys_getsockopt
PTR sys_clone /* 6055 */ PTR sys_clone /* 6055 */
PTR sys_fork PTR sys_fork
PTR sys32_execve PTR compat_sys_execve
PTR sys_exit PTR sys_exit
PTR compat_sys_wait4 PTR compat_sys_wait4
PTR sys_kill /* 6060 */ PTR sys_kill /* 6060 */
......
...@@ -203,7 +203,7 @@ sys_call_table: ...@@ -203,7 +203,7 @@ sys_call_table:
PTR sys_creat PTR sys_creat
PTR sys_link PTR sys_link
PTR sys_unlink /* 4010 */ PTR sys_unlink /* 4010 */
PTR sys32_execve PTR compat_sys_execve
PTR sys_chdir PTR sys_chdir
PTR compat_sys_time PTR compat_sys_time
PTR sys_mknod PTR sys_mknod
......
...@@ -127,28 +127,6 @@ _sys_clone(nabi_no_regargs struct pt_regs regs) ...@@ -127,28 +127,6 @@ _sys_clone(nabi_no_regargs struct pt_regs regs)
parent_tidptr, child_tidptr); parent_tidptr, child_tidptr);
} }
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys_execve(nabi_no_regargs struct pt_regs regs)
{
int error;
struct filename *filename;
filename = getname((const char __user *) (long)regs.regs[4]);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename->name,
(const char __user *const __user *) (long)regs.regs[5],
(const char __user *const __user *) (long)regs.regs[6],
&regs);
putname(filename);
out:
return error;
}
SYSCALL_DEFINE1(set_thread_area, unsigned long, addr) SYSCALL_DEFINE1(set_thread_area, unsigned long, addr)
{ {
struct thread_info *ti = task_thread_info(current); struct thread_info *ti = task_thread_info(current);
...@@ -313,34 +291,3 @@ asmlinkage void bad_stack(void) ...@@ -313,34 +291,3 @@ asmlinkage void bad_stack(void)
{ {
do_exit(SIGSEGV); do_exit(SIGSEGV);
} }
/*
* Do a system call from kernel instead of calling sys_execve so we
* end up with proper pt_regs.
*/
int kernel_execve(const char *filename,
const char *const argv[],
const char *const envp[])
{
register unsigned long __a0 asm("$4") = (unsigned long) filename;
register unsigned long __a1 asm("$5") = (unsigned long) argv;
register unsigned long __a2 asm("$6") = (unsigned long) envp;
register unsigned long __a3 asm("$7");
unsigned long __v0;
__asm__ volatile (" \n"
" .set noreorder \n"
" li $2, %5 # __NR_execve \n"
" syscall \n"
" move %0, $2 \n"
" .set reorder \n"
: "=&r" (__v0), "=r" (__a3)
: "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_execve)
: "$2", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24",
"memory");
if (__a3 == 0)
return __v0;
return -__v0;
}
...@@ -9,6 +9,7 @@ config MN10300 ...@@ -9,6 +9,7 @@ config MN10300
select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select GENERIC_KERNEL_THREAD select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
config AM33_2 config AM33_2
......
...@@ -44,7 +44,6 @@ ...@@ -44,7 +44,6 @@
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_SYS_EXECVE #define __ARCH_WANT_SYS_EXECVE
#define __ARCH_WANT_KERNEL_EXECVE
/* /*
* "Conditional" syscalls * "Conditional" syscalls
......
...@@ -60,13 +60,8 @@ ENTRY(ret_from_kernel_thread) ...@@ -60,13 +60,8 @@ ENTRY(ret_from_kernel_thread)
mov (REG_D0,fp),d0 mov (REG_D0,fp),d0
mov (REG_A0,fp),a0 mov (REG_A0,fp),a0
calls (a0) calls (a0)
jmp sys_exit
ENTRY(ret_from_kernel_execve)
add -12,d0 /* pt_regs -> frame */
mov d0,sp
GET_THREAD_INFO a2
clr d0 clr d0
mov d0,(REG_D0,fp)
jmp syscall_exit jmp syscall_exit
############################################################################### ###############################################################################
......
...@@ -22,6 +22,8 @@ config OPENRISC ...@@ -22,6 +22,8 @@ config OPENRISC
select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNCPY_FROM_USER
select GENERIC_STRNLEN_USER select GENERIC_STRNLEN_USER
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
config MMU config MMU
def_bool y def_bool y
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#define sys_mmap2 sys_mmap_pgoff #define sys_mmap2 sys_mmap_pgoff
#define __ARCH_WANT_SYS_EXECVE
#include <asm-generic/unistd.h> #include <asm-generic/unistd.h>
#define __NR_or1k_atomic __NR_arch_specific_syscall #define __NR_or1k_atomic __NR_arch_specific_syscall
......
...@@ -894,6 +894,16 @@ ENTRY(ret_from_fork) ...@@ -894,6 +894,16 @@ ENTRY(ret_from_fork)
l.jal schedule_tail l.jal schedule_tail
l.nop 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 */ /* _syscall_returns expect r11 to contain return value */
l.lwz r11,PT_GPR11(r1) l.lwz r11,PT_GPR11(r1)
...@@ -915,26 +925,6 @@ ENTRY(ret_from_fork) ...@@ -915,26 +925,6 @@ ENTRY(ret_from_fork)
l.j _syscall_return l.j _syscall_return
l.nop 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 ] === */ /* ========================================================[ switch ] === */
/* /*
...@@ -1044,8 +1034,13 @@ ENTRY(_switch) ...@@ -1044,8 +1034,13 @@ ENTRY(_switch)
/* Unwind stack to pre-switch state */ /* Unwind stack to pre-switch state */
l.addi r1,r1,(INT_FRAME_SIZE) l.addi r1,r1,(INT_FRAME_SIZE)
/* Return via the link-register back to where we 'came from', where that can be /* Return via the link-register back to where we 'came from', where
* either schedule() or return_from_fork()... */ * 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.jr r9
l.nop l.nop
...@@ -1088,10 +1083,6 @@ ENTRY(sys_fork) ...@@ -1088,10 +1083,6 @@ ENTRY(sys_fork)
l.j _fork_save_extra_regs_and_call l.j _fork_save_extra_regs_and_call
l.addi r3,r1,0 l.addi r3,r1,0
ENTRY(sys_execve)
l.j _sys_execve
l.addi r6,r1,0
ENTRY(sys_sigaltstack) ENTRY(sys_sigaltstack)
l.j _sys_sigaltstack l.j _sys_sigaltstack
l.addi r5,r1,0 l.addi r5,r1,0
......
...@@ -109,66 +109,82 @@ void release_thread(struct task_struct *dead_task) ...@@ -109,66 +109,82 @@ void release_thread(struct task_struct *dead_task)
*/ */
extern asmlinkage void ret_from_fork(void); 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 int
copy_thread(unsigned long clone_flags, unsigned long usp, 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 *regs)
{ {
struct pt_regs *childregs; struct pt_regs *userregs;
struct pt_regs *kregs; struct pt_regs *kregs;
unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE; unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE;
struct thread_info *ti;
unsigned long top_of_kernel_stack; unsigned long top_of_kernel_stack;
top_of_kernel_stack = sp; top_of_kernel_stack = sp;
p->set_child_tid = p->clear_child_tid = NULL; p->set_child_tid = p->clear_child_tid = NULL;
/* Copy registers */ /* Locate userspace context on stack... */
/* redzone */ sp -= STACK_FRAME_OVERHEAD; /* redzone */
sp -= STACK_FRAME_OVERHEAD;
sp -= sizeof(struct pt_regs); sp -= sizeof(struct pt_regs);
childregs = (struct pt_regs *)sp; userregs = (struct pt_regs *) sp;
/* Copy parent registers */ /* ...and kernel context */
*childregs = *regs; sp -= STACK_FRAME_OVERHEAD; /* redzone */
sp -= sizeof(struct pt_regs);
kregs = (struct pt_regs *)sp;
if ((childregs->sr & SPR_SR_SM) == 1) { if (unlikely(p->flags & PF_KTHREAD)) {
/* for kernel thread, set `current_thread_info' memset(kregs, 0, sizeof(struct pt_regs));
* and stackptr in new task kregs->gpr[20] = usp; /* fn, kernel thread */
*/ kregs->gpr[22] = arg;
childregs->sp = (unsigned long)task_stack_page(p) + THREAD_SIZE;
childregs->gpr[10] = (unsigned long)task_thread_info(p);
} else { } else {
childregs->sp = usp; *userregs = *regs;
}
childregs->gpr[11] = 0; /* Result from fork() */ userregs->sp = usp;
userregs->gpr[11] = 0; /* Result from fork() */
/* kregs->gpr[20] = 0; /* Userspace thread */
* 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;
ti = task_thread_info(p);
ti->ksp = sp;
/* kregs->sp must store the location of the 'pre-switch' kernel stack /*
* pointer... for a newly forked process, this is simply the top of * _switch wants the kernel stack page in pt_regs->sp so that it
* the kernel stack. * can restore it to thread_info->ksp... see _switch for details.
*/ */
kregs->sp = top_of_kernel_stack; 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; kregs->gpr[9] = (unsigned long)ret_from_fork;
task_thread_info(p)->ksp = (unsigned long)kregs;
return 0; return 0;
} }
...@@ -177,16 +193,14 @@ copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -177,16 +193,14 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
*/ */
void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) 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); set_fs(USER_DS);
memset(regs->gpr, 0, sizeof(regs->gpr)); memset(regs, 0, sizeof(struct pt_regs));
regs->pc = pc; regs->pc = pc;
regs->sr = sr; regs->sr = sr;
regs->sp = sp; regs->sp = sp;
/* printk("start thread, ksp = %lx\n", current_thread_info()->ksp);*/
} }
/* Fill in the fpu structure for a core dump. */ /* Fill in the fpu structure for a core dump. */
...@@ -237,74 +251,9 @@ void dump_elf_thread(elf_greg_t *dest, struct pt_regs* regs) ...@@ -237,74 +251,9 @@ void dump_elf_thread(elf_greg_t *dest, struct pt_regs* regs)
dest[35] = 0; 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(&regs, 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, &regs, 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) unsigned long get_wchan(struct task_struct *p)
{ {
/* TODO */ /* TODO */
return 0; 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;
}
...@@ -22,6 +22,8 @@ config PARISC ...@@ -22,6 +22,8 @@ config PARISC
select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNCPY_FROM_USER
select HAVE_MOD_ARCH_SPECIFIC select HAVE_MOD_ARCH_SPECIFIC
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select GENERIC_KERNEL_THREAD
select GENERIC_KERNEL_EXECVE
help help
The PA-RISC microprocessor is designed by Hewlett-Packard and used The PA-RISC microprocessor is designed by Hewlett-Packard and used
......
...@@ -163,6 +163,7 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ ...@@ -163,6 +163,7 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_SYS_EXECVE
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
......
...@@ -707,60 +707,10 @@ ENTRY(end_fault_vector) ...@@ -707,60 +707,10 @@ ENTRY(end_fault_vector)
.import handle_interruption,code .import handle_interruption,code
.import do_cpu_irq_mask,code .import do_cpu_irq_mask,code
/*
* r26 = function to be called
* r25 = argument to pass in
* r24 = flags for do_fork()
*
* Kernel threads don't ever return, so they don't need
* a true register context. We just save away the arguments
* for copy_thread/ret_ to properly set up the child.
*/
#define CLONE_VM 0x100 /* Must agree with <linux/sched.h> */
#define CLONE_UNTRACED 0x00800000
.import do_fork
ENTRY(__kernel_thread)
STREG %r2, -RP_OFFSET(%r30)
copy %r30, %r1
ldo PT_SZ_ALGN(%r30),%r30
#ifdef CONFIG_64BIT
/* Yo, function pointers in wide mode are little structs... -PB */
ldd 24(%r26), %r2
STREG %r2, PT_GR27(%r1) /* Store childs %dp */
ldd 16(%r26), %r26
STREG %r22, PT_GR22(%r1) /* save r22 (arg5) */
copy %r0, %r22 /* user_tid */
#endif
STREG %r26, PT_GR26(%r1) /* Store function & argument for child */
STREG %r25, PT_GR25(%r1)
ldil L%CLONE_UNTRACED, %r26
ldo CLONE_VM(%r26), %r26 /* Force CLONE_VM since only init_mm */
or %r26, %r24, %r26 /* will have kernel mappings. */
ldi 1, %r25 /* stack_start, signals kernel thread */
stw %r0, -52(%r30) /* user_tid */
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
BL do_fork, %r2
copy %r1, %r24 /* pt_regs */
/* Parent Returns here */
LDREG -PT_SZ_ALGN-RP_OFFSET(%r30), %r2
ldo -PT_SZ_ALGN(%r30), %r30
bv %r0(%r2)
nop
ENDPROC(__kernel_thread)
/* /*
* Child Returns here * Child Returns here
* *
* copy_thread moved args from temp save area set up above * copy_thread moved args into task save area.
* into task save area.
*/ */
ENTRY(ret_from_kernel_thread) ENTRY(ret_from_kernel_thread)
...@@ -769,51 +719,17 @@ ENTRY(ret_from_kernel_thread) ...@@ -769,51 +719,17 @@ ENTRY(ret_from_kernel_thread)
BL schedule_tail, %r2 BL schedule_tail, %r2
nop nop
LDREG TI_TASK-THREAD_SZ_ALGN(%r30), %r1 LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1
LDREG TASK_PT_GR25(%r1), %r26 LDREG TASK_PT_GR25(%r1), %r26
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
LDREG TASK_PT_GR27(%r1), %r27 LDREG TASK_PT_GR27(%r1), %r27
LDREG TASK_PT_GR22(%r1), %r22
#endif #endif
LDREG TASK_PT_GR26(%r1), %r1 LDREG TASK_PT_GR26(%r1), %r1
ble 0(%sr7, %r1) ble 0(%sr7, %r1)
copy %r31, %r2 copy %r31, %r2
b finish_child_return
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
loadgp /* Thread could have been in a module */
#endif
#ifndef CONFIG_64BIT
b sys_exit
#else
load32 sys_exit, %r1
bv %r0(%r1)
#endif
ldi 0, %r26
ENDPROC(ret_from_kernel_thread)
.import sys_execve, code
ENTRY(__execve)
copy %r2, %r15
copy %r30, %r16
ldo PT_SZ_ALGN(%r30), %r30
STREG %r26, PT_GR26(%r16)
STREG %r25, PT_GR25(%r16)
STREG %r24, PT_GR24(%r16)
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
BL sys_execve, %r2
copy %r16, %r26
cmpib,=,n 0,%r28,intr_return /* forward */
/* yes, this will trap and die. */
copy %r15, %r2
copy %r16, %r30
bv %r0(%r2)
nop nop
ENDPROC(__execve) ENDPROC(ret_from_kernel_thread)
/* /*
...@@ -1776,49 +1692,27 @@ ENTRY(sys_fork_wrapper) ...@@ -1776,49 +1692,27 @@ ENTRY(sys_fork_wrapper)
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1 LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1
ldo TASK_REGS(%r1),%r1 ldo TASK_REGS(%r1),%r1
reg_save %r1 reg_save %r1
mfctl %cr27, %r3 mfctl %cr27, %r28
STREG %r3, PT_CR27(%r1) STREG %r28, PT_CR27(%r1)
STREG %r2,-RP_OFFSET(%r30)
ldo FRAME_SIZE(%r30),%r30
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
/* These are call-clobbered registers and therefore
also syscall-clobbered (we hope). */
STREG %r2,PT_GR19(%r1) /* save for child */
STREG %r30,PT_GR21(%r1)
LDREG PT_GR30(%r1),%r25 LDREG PT_GR30(%r1),%r25
copy %r1,%r24 copy %r1,%r24
BL sys_clone,%r2 b sys_clone
ldi SIGCHLD,%r26 ldi SIGCHLD,%r26
LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2
wrapper_exit:
ldo -FRAME_SIZE(%r30),%r30 /* get the stackframe */
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
ldo TASK_REGS(%r1),%r1 /* get pt regs */
LDREG PT_CR27(%r1), %r3
mtctl %r3, %cr27
reg_restore %r1
/* strace expects syscall # to be preserved in r20 */
ldi __NR_fork,%r20
bv %r0(%r2)
STREG %r20,PT_GR20(%r1)
ENDPROC(sys_fork_wrapper) ENDPROC(sys_fork_wrapper)
/* Set the return value for the child */ /* Set the return value for the child */
ENTRY(child_return) ENTRY(child_return)
BL schedule_tail, %r2 BL schedule_tail, %r2
nop nop
finish_child_return:
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1
ldo TASK_REGS(%r1),%r1 /* get pt regs */
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE-FRAME_SIZE(%r30), %r1 LDREG PT_CR27(%r1), %r3
LDREG TASK_PT_GR19(%r1),%r2 mtctl %r3, %cr27
b wrapper_exit reg_restore %r1
b syscall_exit
copy %r0,%r28 copy %r0,%r28
ENDPROC(child_return) ENDPROC(child_return)
...@@ -1827,23 +1721,10 @@ ENTRY(sys_clone_wrapper) ...@@ -1827,23 +1721,10 @@ ENTRY(sys_clone_wrapper)
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
ldo TASK_REGS(%r1),%r1 /* get pt regs */ ldo TASK_REGS(%r1),%r1 /* get pt regs */
reg_save %r1 reg_save %r1
mfctl %cr27, %r3 mfctl %cr27, %r28
STREG %r3, PT_CR27(%r1) STREG %r28, PT_CR27(%r1)
b sys_clone
STREG %r2,-RP_OFFSET(%r30)
ldo FRAME_SIZE(%r30),%r30
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
/* WARNING - Clobbers r19 and r21, userspace must save these! */
STREG %r2,PT_GR19(%r1) /* save for child */
STREG %r30,PT_GR21(%r1)
BL sys_clone,%r2
copy %r1,%r24 copy %r1,%r24
b wrapper_exit
LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2
ENDPROC(sys_clone_wrapper) ENDPROC(sys_clone_wrapper)
...@@ -1851,72 +1732,14 @@ ENTRY(sys_vfork_wrapper) ...@@ -1851,72 +1732,14 @@ ENTRY(sys_vfork_wrapper)
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
ldo TASK_REGS(%r1),%r1 /* get pt regs */ ldo TASK_REGS(%r1),%r1 /* get pt regs */
reg_save %r1 reg_save %r1
mfctl %cr27, %r3 mfctl %cr27, %r28
STREG %r3, PT_CR27(%r1) STREG %r28, PT_CR27(%r1)
STREG %r2,-RP_OFFSET(%r30)
ldo FRAME_SIZE(%r30),%r30
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
STREG %r2,PT_GR19(%r1) /* save for child */
STREG %r30,PT_GR21(%r1)
BL sys_vfork,%r2 b sys_vfork
copy %r1,%r26 copy %r1,%r26
b wrapper_exit
LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2
ENDPROC(sys_vfork_wrapper) ENDPROC(sys_vfork_wrapper)
.macro execve_wrapper execve
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
ldo TASK_REGS(%r1),%r1 /* get pt regs */
/*
* Do we need to save/restore r3-r18 here?
* I don't think so. why would new thread need old
* threads registers?
*/
/* %arg0 - %arg3 are already saved for us. */
STREG %r2,-RP_OFFSET(%r30)
ldo FRAME_SIZE(%r30),%r30
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
BL \execve,%r2
copy %r1,%arg0
ldo -FRAME_SIZE(%r30),%r30
LDREG -RP_OFFSET(%r30),%r2
/* If exec succeeded we need to load the args */
ldo -1024(%r0),%r1
cmpb,>>= %r28,%r1,error_\execve
copy %r2,%r19
error_\execve:
bv %r0(%r19)
nop
.endm
.import sys_execve
ENTRY(sys_execve_wrapper)
execve_wrapper sys_execve
ENDPROC(sys_execve_wrapper)
#ifdef CONFIG_64BIT
.import sys32_execve
ENTRY(sys32_execve_wrapper)
execve_wrapper sys32_execve
ENDPROC(sys32_execve_wrapper)
#endif
ENTRY(sys_rt_sigreturn_wrapper) ENTRY(sys_rt_sigreturn_wrapper)
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r26 LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r26
ldo TASK_REGS(%r26),%r26 /* get pt regs */ ldo TASK_REGS(%r26),%r26 /* get pt regs */
......
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/asm-offsets.h> #include <asm/asm-offsets.h>
#include <asm/assembly.h>
#include <asm/pdc.h> #include <asm/pdc.h>
#include <asm/pdc_chassis.h> #include <asm/pdc_chassis.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
...@@ -164,23 +165,6 @@ void machine_power_off(void) ...@@ -164,23 +165,6 @@ void machine_power_off(void)
void (*pm_power_off)(void) = machine_power_off; void (*pm_power_off)(void) = machine_power_off;
EXPORT_SYMBOL(pm_power_off); EXPORT_SYMBOL(pm_power_off);
/*
* Create a kernel thread
*/
extern pid_t __kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
/*
* FIXME: Once we are sure we don't need any debug here,
* kernel_thread can become a #define.
*/
return __kernel_thread(fn, arg, flags);
}
EXPORT_SYMBOL(kernel_thread);
/* /*
* Free current thread data structures etc.. * Free current thread data structures etc..
*/ */
...@@ -256,8 +240,8 @@ sys_vfork(struct pt_regs *regs) ...@@ -256,8 +240,8 @@ sys_vfork(struct pt_regs *regs)
int int
copy_thread(unsigned long clone_flags, unsigned long usp, copy_thread(unsigned long clone_flags, unsigned long usp,
unsigned long unused, /* in ia64 this is "user_stack_size" */ unsigned long arg,
struct task_struct * p, struct pt_regs * pregs) struct task_struct *p, struct pt_regs *pregs)
{ {
struct pt_regs * cregs = &(p->thread.regs); struct pt_regs * cregs = &(p->thread.regs);
void *stack = task_stack_page(p); void *stack = task_stack_page(p);
...@@ -270,48 +254,32 @@ copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -270,48 +254,32 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
#ifdef CONFIG_HPUX #ifdef CONFIG_HPUX
extern void * const hpux_child_return; extern void * const hpux_child_return;
#endif #endif
if (unlikely(p->flags & PF_KTHREAD)) {
memset(cregs, 0, sizeof(struct pt_regs));
if (!usp) /* idle thread */
return 0;
*cregs = *pregs;
/* Set the return value for the child. Note that this is not
actually restored by the syscall exit path, but we put it
here for consistency in case of signals. */
cregs->gr[28] = 0; /* child */
/*
* We need to differentiate between a user fork and a
* kernel fork. We can't use user_mode, because the
* the syscall path doesn't save iaoq. Right now
* We rely on the fact that kernel_thread passes
* in zero for usp.
*/
if (usp == 1) {
/* kernel thread */ /* kernel thread */
cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN;
/* Must exit via ret_from_kernel_thread in order /* Must exit via ret_from_kernel_thread in order
* to call schedule_tail() * to call schedule_tail()
*/ */
cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN + FRAME_SIZE;
cregs->kpc = (unsigned long) &ret_from_kernel_thread; cregs->kpc = (unsigned long) &ret_from_kernel_thread;
/* /*
* Copy function and argument to be called from * Copy function and argument to be called from
* ret_from_kernel_thread. * ret_from_kernel_thread.
*/ */
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
cregs->gr[27] = pregs->gr[27]; cregs->gr[27] = ((unsigned long *)usp)[3];
cregs->gr[26] = ((unsigned long *)usp)[2];
#else
cregs->gr[26] = usp;
#endif #endif
cregs->gr[26] = pregs->gr[26]; cregs->gr[25] = arg;
cregs->gr[25] = pregs->gr[25];
} else { } else {
/* user thread */ /* user thread */
/*
* Note that the fork wrappers are responsible
* for setting gr[21].
*/
/* Use same stack depth as parent */
cregs->ksp = (unsigned long)stack
+ (pregs->gr[21] & (THREAD_SIZE - 1));
cregs->gr[30] = usp; cregs->gr[30] = usp;
cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN + FRAME_SIZE;
if (personality(p->personality) == PER_HPUX) { if (personality(p->personality) == PER_HPUX) {
#ifdef CONFIG_HPUX #ifdef CONFIG_HPUX
cregs->kpc = (unsigned long) &hpux_child_return; cregs->kpc = (unsigned long) &hpux_child_return;
...@@ -324,7 +292,6 @@ copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -324,7 +292,6 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
/* Setup thread TLS area from the 4th parameter in clone */ /* Setup thread TLS area from the 4th parameter in clone */
if (clone_flags & CLONE_SETTLS) if (clone_flags & CLONE_SETTLS)
cregs->cr27 = pregs->gr[23]; cregs->cr27 = pregs->gr[23];
} }
return 0; return 0;
...@@ -335,39 +302,6 @@ unsigned long thread_saved_pc(struct task_struct *t) ...@@ -335,39 +302,6 @@ unsigned long thread_saved_pc(struct task_struct *t)
return t->thread.regs.kpc; return t->thread.regs.kpc;
} }
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys_execve(struct pt_regs *regs)
{
int error;
struct filename *filename;
filename = getname((const char __user *) regs->gr[26]);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename->name,
(const char __user *const __user *) regs->gr[25],
(const char __user *const __user *) regs->gr[24],
regs);
putname(filename);
out:
return error;
}
extern int __execve(const char *filename,
const char *const argv[],
const char *const envp[], struct task_struct *task);
int kernel_execve(const char *filename,
const char *const argv[],
const char *const envp[])
{
return __execve(filename, argv, envp, current);
}
unsigned long unsigned long
get_wchan(struct task_struct *p) get_wchan(struct task_struct *p)
{ {
......
...@@ -53,28 +53,6 @@ ...@@ -53,28 +53,6 @@
#define DBG(x) #define DBG(x)
#endif #endif
/*
* sys32_execve() executes a new program.
*/
asmlinkage int sys32_execve(struct pt_regs *regs)
{
int error;
struct filename *filename;
DBG(("sys32_execve(%p) r26 = 0x%lx\n", regs, regs->gr[26]));
filename = getname((const char __user *) regs->gr[26]);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = compat_do_execve(filename->name, compat_ptr(regs->gr[25]),
compat_ptr(regs->gr[24]), regs);
putname(filename);
out:
return error;
}
asmlinkage long sys32_unimplemented(int r26, int r25, int r24, int r23, asmlinkage long sys32_unimplemented(int r26, int r25, int r24, int r23,
int r22, int r21, int r20) int r22, int r21, int r20)
{ {
......
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
ENTRY_SAME(creat) ENTRY_SAME(creat)
ENTRY_SAME(link) ENTRY_SAME(link)
ENTRY_SAME(unlink) /* 10 */ ENTRY_SAME(unlink) /* 10 */
ENTRY_DIFF(execve_wrapper) ENTRY_COMP(execve)
ENTRY_SAME(chdir) ENTRY_SAME(chdir)
/* See comments in kernel/time.c!!! Maybe we don't need this? */ /* See comments in kernel/time.c!!! Maybe we don't need this? */
ENTRY_COMP(time) ENTRY_COMP(time)
......
...@@ -144,6 +144,7 @@ config PPC ...@@ -144,6 +144,7 @@ config PPC
select GENERIC_KERNEL_THREAD select GENERIC_KERNEL_THREAD
select HAVE_MOD_ARCH_SPECIFIC select HAVE_MOD_ARCH_SPECIFIC
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select GENERIC_KERNEL_EXECVE
config EARLY_PRINTK config EARLY_PRINTK
bool bool
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment