diff --git a/[refs] b/[refs] index c8ecb7538e0a..a37b59b21b44 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 94b28de47ff713dcf1a4581084cd90f44df07b44 +refs/heads/master: a44e060fc523c379341e35a981c09c3953cf4ba4 diff --git a/trunk/arch/arm64/Kconfig b/trunk/arch/arm64/Kconfig index 75b212d5db9d..7ff68c946073 100644 --- a/trunk/arch/arm64/Kconfig +++ b/trunk/arch/arm64/Kconfig @@ -6,8 +6,6 @@ config ARM64 select GENERIC_IOMAP select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW - select GENERIC_KERNEL_EXECVE - select GENERIC_KERNEL_THREAD select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL select HARDIRQS_SW_RESEND diff --git a/trunk/arch/arm64/include/asm/processor.h b/trunk/arch/arm64/include/asm/processor.h index d6331acaf64e..39a208a392f7 100644 --- a/trunk/arch/arm64/include/asm/processor.h +++ b/trunk/arch/arm64/include/asm/processor.h @@ -136,6 +136,11 @@ unsigned long get_wchan(struct task_struct *p); extern struct task_struct *cpu_switch_to(struct task_struct *prev, 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) \ ((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1) diff --git a/trunk/arch/arm64/include/asm/syscalls.h b/trunk/arch/arm64/include/asm/syscalls.h index 81680a0ae913..09ff33572aab 100644 --- a/trunk/arch/arm64/include/asm/syscalls.h +++ b/trunk/arch/arm64/include/asm/syscalls.h @@ -23,6 +23,9 @@ /* * 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, diff --git a/trunk/arch/arm64/include/asm/unistd.h b/trunk/arch/arm64/include/asm/unistd.h index b40dc6b69848..63f853f8b718 100644 --- a/trunk/arch/arm64/include/asm/unistd.h +++ b/trunk/arch/arm64/include/asm/unistd.h @@ -26,5 +26,4 @@ #define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_COMPAT_SYS_SENDFILE #endif -#define __ARCH_WANT_SYS_EXECVE #include diff --git a/trunk/arch/arm64/include/asm/unistd32.h b/trunk/arch/arm64/include/asm/unistd32.h index 9035e6add3e4..6d909faebf28 100644 --- a/trunk/arch/arm64/include/asm/unistd32.h +++ b/trunk/arch/arm64/include/asm/unistd32.h @@ -32,7 +32,7 @@ __SYSCALL(7, sys_ni_syscall) /* 7 was sys_waitpid */ __SYSCALL(8, sys_creat) __SYSCALL(9, sys_link) __SYSCALL(10, sys_unlink) -__SYSCALL(11, compat_sys_execve) +__SYSCALL(11, compat_sys_execve_wrapper) __SYSCALL(12, sys_chdir) __SYSCALL(13, sys_ni_syscall) /* 13 was sys_time */ __SYSCALL(14, sys_mknod) diff --git a/trunk/arch/arm64/kernel/entry.S b/trunk/arch/arm64/kernel/entry.S index 616531862d52..a6f3f7da6880 100644 --- a/trunk/arch/arm64/kernel/entry.S +++ b/trunk/arch/arm64/kernel/entry.S @@ -594,7 +594,7 @@ work_resched: /* * "slow" syscall return path. */ -ret_to_user: +ENTRY(ret_to_user) disable_irq // disable interrupts ldr x1, [tsk, #TI_FLAGS] and x2, x1, #_TIF_WORK_MASK @@ -611,10 +611,7 @@ ENDPROC(ret_to_user) */ ENTRY(ret_from_fork) bl schedule_tail - cbz x19, 1f // not a kernel thread - mov x0, x20 - blr x19 -1: get_thread_info tsk + get_thread_info tsk b ret_to_user ENDPROC(ret_from_fork) @@ -676,6 +673,11 @@ __sys_trace_return: /* * 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 diff --git a/trunk/arch/arm64/kernel/process.c b/trunk/arch/arm64/kernel/process.c index bf615e212c6c..f22965ea1cfc 100644 --- a/trunk/arch/arm64/kernel/process.c +++ b/trunk/arch/arm64/kernel/process.c @@ -240,35 +240,27 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *childregs = task_pt_regs(p); unsigned long tls = p->thread.tp_value; - memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context)); + *childregs = *regs; + childregs->regs[0] = 0; - if (likely(regs)) { - *childregs = *regs; - childregs->regs[0] = 0; - if (is_compat_thread(task_thread_info(p))) { - childregs->compat_sp = stack_start; - } else { - /* - * Read the current TLS pointer from tpidr_el0 as it may be - * out-of-sync with the saved value. - */ - asm("mrs %0, tpidr_el0" : "=r" (tls)); - childregs->sp = stack_start; - } + if (is_compat_thread(task_thread_info(p))) + childregs->compat_sp = stack_start; + else { /* - * If a TLS pointer was passed to clone (4th argument), use it - * for the new thread. + * Read the current TLS pointer from tpidr_el0 as it may be + * out-of-sync with the saved value. */ - if (clone_flags & CLONE_SETTLS) - 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; + asm("mrs %0, tpidr_el0" : "=r" (tls)); + childregs->sp = stack_start; } - p->thread.cpu_context.pc = (unsigned long)ret_from_fork; + + memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context)); p->thread.cpu_context.sp = (unsigned long)childregs; + p->thread.cpu_context.pc = (unsigned long)ret_from_fork; + + /* If a TLS pointer was passed to clone, use that for the new thread. */ + if (clone_flags & CLONE_SETTLS) + tls = regs->regs[3]; p->thread.tp_value = tls; ptrace_hw_copy_thread(p); @@ -335,6 +327,43 @@ int dump_fpu (struct pt_regs *regs, struct user_fp *fp) } EXPORT_SYMBOL(dump_fpu); +/* + * 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(®s, 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, ®s, 0, NULL, NULL); +} +EXPORT_SYMBOL(kernel_thread); + unsigned long get_wchan(struct task_struct *p) { struct stackframe frame; diff --git a/trunk/arch/arm64/kernel/sys.c b/trunk/arch/arm64/kernel/sys.c index 9c77c0bacc1d..b120df37de35 100644 --- a/trunk/arch/arm64/kernel/sys.c +++ b/trunk/arch/arm64/kernel/sys.c @@ -41,6 +41,70 @@ asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp, 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(®s, 0, sizeof(struct pt_regs)); + ret = do_execve(filename, + (const char __user *const __user *)argv, + (const char __user *const __user *)envp, ®s); + 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" (®s), + "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, unsigned long prot, unsigned long flags, unsigned long fd, off_t off) @@ -54,6 +118,7 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len, /* * 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_sigaltstack sys_sigaltstack_wrapper diff --git a/trunk/arch/arm64/kernel/sys32.S b/trunk/arch/arm64/kernel/sys32.S index 92145d402cf1..54c4aec47a08 100644 --- a/trunk/arch/arm64/kernel/sys32.S +++ b/trunk/arch/arm64/kernel/sys32.S @@ -36,6 +36,11 @@ compat_sys_vfork_wrapper: 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 diff --git a/trunk/arch/arm64/kernel/sys_compat.c b/trunk/arch/arm64/kernel/sys_compat.c index d140b73a8bc4..906e3bd270b0 100644 --- a/trunk/arch/arm64/kernel/sys_compat.c +++ b/trunk/arch/arm64/kernel/sys_compat.c @@ -49,6 +49,24 @@ asmlinkage int compat_sys_vfork(struct pt_regs *regs) 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, struct compat_timespec __user *interval) { diff --git a/trunk/arch/c6x/Kconfig b/trunk/arch/c6x/Kconfig index 66eab3703c75..aee1b569ee6e 100644 --- a/trunk/arch/c6x/Kconfig +++ b/trunk/arch/c6x/Kconfig @@ -18,7 +18,6 @@ config C6X select OF_EARLY_FLATTREE select GENERIC_CLOCKEVENTS select GENERIC_KERNEL_THREAD - select GENERIC_KERNEL_EXECVE select MODULES_USE_ELF_RELA config MMU diff --git a/trunk/arch/c6x/include/uapi/asm/unistd.h b/trunk/arch/c6x/include/uapi/asm/unistd.h index 625beafb9cd1..4ff747d12dad 100644 --- a/trunk/arch/c6x/include/uapi/asm/unistd.h +++ b/trunk/arch/c6x/include/uapi/asm/unistd.h @@ -14,6 +14,7 @@ * more details. */ +#define __ARCH_WANT_KERNEL_EXECVE #define __ARCH_WANT_SYS_EXECVE /* Use the standard ABI for syscalls. */ diff --git a/trunk/arch/c6x/kernel/entry.S b/trunk/arch/c6x/kernel/entry.S index 75f6f36472cf..5449c36018fe 100644 --- a/trunk/arch/c6x/kernel/entry.S +++ b/trunk/arch/c6x/kernel/entry.S @@ -413,9 +413,19 @@ ENTRY(ret_from_kernel_thread) 0: B .S2 B10 /* call fn */ LDW .D2T1 *+SP(REGS_A1+8),A4 /* get arg */ - ADDKPC .S2 ret_from_fork_2,B3,3 + MVKL .S2 sys_exit,B11 + MVKH .S2 sys_exit,B11 + ADDKPC .S2 0f,B3,1 +0: + BNOP .S2 B11,5 /* jump to sys_exit */ 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() ;; int6 is used for syscalls (see _system_call entry) diff --git a/trunk/arch/ia64/Kconfig b/trunk/arch/ia64/Kconfig index 670600468128..3279646120e3 100644 --- a/trunk/arch/ia64/Kconfig +++ b/trunk/arch/ia64/Kconfig @@ -42,8 +42,6 @@ config IA64 select GENERIC_TIME_VSYSCALL_OLD select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA - select GENERIC_KERNEL_THREAD - select GENERIC_KERNEL_EXECVE default y help The Itanium Processor Family is Intel's 64-bit successor to diff --git a/trunk/arch/ia64/include/asm/processor.h b/trunk/arch/ia64/include/asm/processor.h index e0a899a1a8a6..944152a50912 100644 --- a/trunk/arch/ia64/include/asm/processor.h +++ b/trunk/arch/ia64/include/asm/processor.h @@ -340,6 +340,22 @@ struct task_struct; */ #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. */ extern unsigned long get_wchan (struct task_struct *p); diff --git a/trunk/arch/ia64/include/asm/unistd.h b/trunk/arch/ia64/include/asm/unistd.h index 1574bca86138..8b3ff2f5b861 100644 --- a/trunk/arch/ia64/include/asm/unistd.h +++ b/trunk/arch/ia64/include/asm/unistd.h @@ -29,7 +29,6 @@ #define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGSUSPEND -#define __ARCH_WANT_SYS_EXECVE #if !defined(__ASSEMBLY__) && !defined(ASSEMBLER) diff --git a/trunk/arch/ia64/kernel/entry.S b/trunk/arch/ia64/kernel/entry.S index 940a67263629..1ccbe12a4d84 100644 --- a/trunk/arch/ia64/kernel/entry.S +++ b/trunk/arch/ia64/kernel/entry.S @@ -61,13 +61,14 @@ ENTRY(ia64_execve) * Allocate 8 input registers since ptrace() may clobber them */ .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) - alloc loc1=ar.pfs,8,2,3,0 + alloc loc1=ar.pfs,8,2,4,0 mov loc0=rp .body mov out0=in0 // filename ;; // stop bit between alloc and call mov out1=in1 // argv mov out2=in2 // envp + add out3=16,sp // regs br.call.sptk.many rp=sys_execve .ret0: cmp4.ge p6,p7=r8,r0 @@ -75,6 +76,7 @@ ENTRY(ia64_execve) sxt4 r8=r8 // return 64-bit result ;; stf.spill [sp]=f0 +(p6) cmp.ne pKStk,pUStk=r0,r0 // a successful execve() lands us in user-mode... mov rp=loc0 (p6) mov ar.pfs=r0 // clear ar.pfs on success (p7) br.ret.sptk.many rp @@ -482,6 +484,19 @@ GLOBAL_ENTRY(prefetch_stack) br.ret.sptk.many rp 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. * We MUST preserve the current register frame throughout this routine @@ -585,27 +600,6 @@ GLOBAL_ENTRY(ia64_strace_leave_kernel) .ret4: br.cond.sptk ia64_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) PT_REGS_UNWIND_INFO(0) { /* @@ -622,7 +616,6 @@ GLOBAL_ENTRY(ia64_ret_from_clone) br.call.sptk.many rp=ia64_invoke_schedule_tail } .ret8: -(pKStk) br.call.sptk.many rp=call_payload adds r2=TI_FLAGS+IA64_TASK_SIZE,r13 ;; ld4 r2=[r2] diff --git a/trunk/arch/ia64/kernel/head.S b/trunk/arch/ia64/kernel/head.S index 4738ff7bd66a..629a250f7c19 100644 --- a/trunk/arch/ia64/kernel/head.S +++ b/trunk/arch/ia64/kernel/head.S @@ -1093,6 +1093,19 @@ GLOBAL_ENTRY(cycle_to_cputime) END(cycle_to_cputime) #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 /* diff --git a/trunk/arch/ia64/kernel/process.c b/trunk/arch/ia64/kernel/process.c index 25543a295ad9..35e106f2ed13 100644 --- a/trunk/arch/ia64/kernel/process.c +++ b/trunk/arch/ia64/kernel/process.c @@ -401,15 +401,64 @@ copy_thread(unsigned long clone_flags, struct pt_regs *child_ptregs; 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_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; 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: */ 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 * registers in the high partition (fph) to be scratch. Since @@ -431,66 +480,8 @@ copy_thread(unsigned long clone_flags, # define THREAD_FLAGS_TO_SET 0 p->thread.flags = ((current->thread.flags & ~THREAD_FLAGS_TO_CLEAR) | THREAD_FLAGS_TO_SET); - 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 if (current->thread.pfm_context) pfm_inherit(p, child_ptregs); @@ -617,6 +608,57 @@ dump_fpu (struct pt_regs *pt, elf_fpregset_t dst) 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(®s, 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, ®s.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(). */ diff --git a/trunk/arch/m68k/Kconfig b/trunk/arch/m68k/Kconfig index 953a7ba5d050..e7c161433eae 100644 --- a/trunk/arch/m68k/Kconfig +++ b/trunk/arch/m68k/Kconfig @@ -16,7 +16,6 @@ config M68K select ARCH_WANT_IPC_PARSE_VERSION select ARCH_USES_GETTIMEOFFSET if MMU && !COLDFIRE select GENERIC_KERNEL_THREAD - select GENERIC_KERNEL_EXECVE select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_REL select MODULES_USE_ELF_RELA diff --git a/trunk/arch/m68k/include/asm/unistd.h b/trunk/arch/m68k/include/asm/unistd.h index 508afdc1d435..c702ad716791 100644 --- a/trunk/arch/m68k/include/asm/unistd.h +++ b/trunk/arch/m68k/include/asm/unistd.h @@ -383,6 +383,7 @@ #define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_EXECVE +#define __ARCH_WANT_KERNEL_EXECVE /* * "Conditional" syscalls diff --git a/trunk/arch/m68k/kernel/entry.S b/trunk/arch/m68k/kernel/entry.S index 45b2f293f8cf..946cb0187751 100644 --- a/trunk/arch/m68k/kernel/entry.S +++ b/trunk/arch/m68k/kernel/entry.S @@ -115,9 +115,16 @@ ENTRY(ret_from_kernel_thread) | a3 contains the kernel thread payload, d7 - its argument movel %d1,%sp@- jsr schedule_tail + GET_CURRENT(%d0) movel %d7,(%sp) jsr %a3@ 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 #if defined(CONFIG_COLDFIRE) || !defined(CONFIG_MMU) diff --git a/trunk/arch/mips/Kconfig b/trunk/arch/mips/Kconfig index 4183e62f178c..dba9390d37cf 100644 --- a/trunk/arch/mips/Kconfig +++ b/trunk/arch/mips/Kconfig @@ -40,8 +40,6 @@ config MIPS select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_REL select MODULES_USE_ELF_RELA if 64BIT - select GENERIC_KERNEL_THREAD - select GENERIC_KERNEL_EXECVE menu "Machine selection" diff --git a/trunk/arch/mips/include/asm/processor.h b/trunk/arch/mips/include/asm/processor.h index d28c41e0887c..5e33fabe354d 100644 --- a/trunk/arch/mips/include/asm/processor.h +++ b/trunk/arch/mips/include/asm/processor.h @@ -310,6 +310,8 @@ struct task_struct; /* Free all resources held by a thread. */ #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); /* diff --git a/trunk/arch/mips/include/asm/ptrace.h b/trunk/arch/mips/include/asm/ptrace.h index cec5e125f7e4..4f5da948a777 100644 --- a/trunk/arch/mips/include/asm/ptrace.h +++ b/trunk/arch/mips/include/asm/ptrace.h @@ -61,10 +61,4 @@ static inline void die_if_kernel(const char *str, struct pt_regs *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 */ diff --git a/trunk/arch/mips/include/asm/unistd.h b/trunk/arch/mips/include/asm/unistd.h index b306e2081cad..9e47cc11aa26 100644 --- a/trunk/arch/mips/include/asm/unistd.h +++ b/trunk/arch/mips/include/asm/unistd.h @@ -20,7 +20,6 @@ #define __ARCH_OMIT_COMPAT_SYS_GETDENTS64 #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_SYS_ALARM -#define __ARCH_WANT_SYS_EXECVE #define __ARCH_WANT_SYS_GETHOSTNAME #define __ARCH_WANT_SYS_IPC #define __ARCH_WANT_SYS_PAUSE diff --git a/trunk/arch/mips/kernel/entry.S b/trunk/arch/mips/kernel/entry.S index 3320cb4ac1d4..a6c133212003 100644 --- a/trunk/arch/mips/kernel/entry.S +++ b/trunk/arch/mips/kernel/entry.S @@ -65,12 +65,6 @@ need_resched: b need_resched #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) jal schedule_tail # a0 = struct task_struct *prev diff --git a/trunk/arch/mips/kernel/linux32.c b/trunk/arch/mips/kernel/linux32.c index 8796dbc7e358..3a21acedf882 100644 --- a/trunk/arch/mips/kernel/linux32.c +++ b/trunk/arch/mips/kernel/linux32.c @@ -3,6 +3,7 @@ * * Copyright (C) 2000 Silicon Graphics, Inc. * Written by Ulf Carlsson (ulfc@engr.sgi.com) + * sys32_execve from ia64/ia32 code, Feb 2000, Kanoj Sarcar (kanoj@sgi.com) */ #include #include @@ -76,6 +77,26 @@ SYSCALL_DEFINE6(32_mmap2, unsigned long, addr, unsigned long, len, 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]), ®s); + putname(filename); + +out: + return error; +} + #define RLIM_INFINITY32 0x7fffffff #define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) diff --git a/trunk/arch/mips/kernel/mips_ksyms.c b/trunk/arch/mips/kernel/mips_ksyms.c index 2d9304c2b54c..3fc1691110dc 100644 --- a/trunk/arch/mips/kernel/mips_ksyms.c +++ b/trunk/arch/mips/kernel/mips_ksyms.c @@ -32,6 +32,8 @@ EXPORT_SYMBOL(memset); EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(kernel_thread); + /* * Functions that operate on entire pages. Mostly used by memory management. */ diff --git a/trunk/arch/mips/kernel/process.c b/trunk/arch/mips/kernel/process.c index d13720ac656f..e9a5fd7277f4 100644 --- a/trunk/arch/mips/kernel/process.c +++ b/trunk/arch/mips/kernel/process.c @@ -84,7 +84,6 @@ void __noreturn cpu_idle(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) { @@ -114,7 +113,7 @@ void flush_thread(void) } int copy_thread(unsigned long clone_flags, unsigned long usp, - unsigned long arg, struct task_struct *p, struct pt_regs *regs) + unsigned long unused, struct task_struct *p, struct pt_regs *regs) { struct thread_info *ti = task_thread_info(p); struct pt_regs *childregs; @@ -137,30 +136,19 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, childregs = (struct pt_regs *) childksp - 1; /* Put the stack after the struct pt_regs. */ 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[7] = 0; /* Clear error flag */ + childregs->regs[2] = 0; /* Child gets zero as return value */ - childregs->regs[29] = usp; - ti->addr_limit = USER_DS; + 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; + ti->addr_limit = USER_DS; + } p->thread.reg29 = (unsigned long) childregs; p->thread.reg31 = (unsigned long) ret_from_fork; @@ -168,6 +156,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, * New tasks lose permission to use the fpu. This accelerates context * 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); #ifdef CONFIG_MIPS_MT_SMTC @@ -232,6 +221,35 @@ int dump_task_fpu(struct task_struct *t, elf_fpregset_t *fpr) 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(®s, 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, ®s, 0, NULL, NULL); +} + /* * */ diff --git a/trunk/arch/mips/kernel/scall64-n32.S b/trunk/arch/mips/kernel/scall64-n32.S index d27ca340d46d..f6ba8381ee01 100644 --- a/trunk/arch/mips/kernel/scall64-n32.S +++ b/trunk/arch/mips/kernel/scall64-n32.S @@ -167,7 +167,7 @@ EXPORT(sysn32_call_table) PTR sys_getsockopt PTR sys_clone /* 6055 */ PTR sys_fork - PTR compat_sys_execve + PTR sys32_execve PTR sys_exit PTR compat_sys_wait4 PTR sys_kill /* 6060 */ diff --git a/trunk/arch/mips/kernel/scall64-o32.S b/trunk/arch/mips/kernel/scall64-o32.S index 9601be6afa3d..53c2d7245764 100644 --- a/trunk/arch/mips/kernel/scall64-o32.S +++ b/trunk/arch/mips/kernel/scall64-o32.S @@ -203,7 +203,7 @@ sys_call_table: PTR sys_creat PTR sys_link PTR sys_unlink /* 4010 */ - PTR compat_sys_execve + PTR sys32_execve PTR sys_chdir PTR compat_sys_time PTR sys_mknod diff --git a/trunk/arch/mips/kernel/syscall.c b/trunk/arch/mips/kernel/syscall.c index c611e2df7767..2bd561bc05ae 100644 --- a/trunk/arch/mips/kernel/syscall.c +++ b/trunk/arch/mips/kernel/syscall.c @@ -127,6 +127,28 @@ _sys_clone(nabi_no_regargs struct pt_regs regs) 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], + ®s); + putname(filename); + +out: + return error; +} + SYSCALL_DEFINE1(set_thread_area, unsigned long, addr) { struct thread_info *ti = task_thread_info(current); @@ -291,3 +313,34 @@ asmlinkage void bad_stack(void) { 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; +} diff --git a/trunk/arch/openrisc/Kconfig b/trunk/arch/openrisc/Kconfig index e7f1a2993f78..05f2ba41ff1a 100644 --- a/trunk/arch/openrisc/Kconfig +++ b/trunk/arch/openrisc/Kconfig @@ -22,8 +22,6 @@ config OPENRISC select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER select MODULES_USE_ELF_RELA - select GENERIC_KERNEL_THREAD - select GENERIC_KERNEL_EXECVE config MMU def_bool y diff --git a/trunk/arch/openrisc/include/uapi/asm/unistd.h b/trunk/arch/openrisc/include/uapi/asm/unistd.h index 5db7bc0fa5a7..437bdbb61b14 100644 --- a/trunk/arch/openrisc/include/uapi/asm/unistd.h +++ b/trunk/arch/openrisc/include/uapi/asm/unistd.h @@ -20,8 +20,6 @@ #define sys_mmap2 sys_mmap_pgoff -#define __ARCH_WANT_SYS_EXECVE - #include #define __NR_or1k_atomic __NR_arch_specific_syscall diff --git a/trunk/arch/openrisc/kernel/entry.S b/trunk/arch/openrisc/kernel/entry.S index c60a09df323f..ddfcaa828b0e 100644 --- a/trunk/arch/openrisc/kernel/entry.S +++ b/trunk/arch/openrisc/kernel/entry.S @@ -894,16 +894,6 @@ ENTRY(ret_from_fork) l.jal schedule_tail l.nop - /* Check if we are a kernel thread */ - l.sfeqi r20,0 - l.bf 1f - l.nop - - /* ...we are a kernel thread so invoke the requested callback */ - l.jalr r20 - l.or r3,r22,r0 - -1: /* _syscall_returns expect r11 to contain return value */ l.lwz r11,PT_GPR11(r1) @@ -925,6 +915,26 @@ ENTRY(ret_from_fork) l.j _syscall_return l.nop +/* Since syscalls don't save call-clobbered registers, the args to + * kernel_thread_helper will need to be passed through callee-saved + * registers and copied to the parameter registers when the thread + * begins running. + * + * See arch/openrisc/kernel/process.c: + * The args are passed as follows: + * arg1 (r3) : passed in r20 + * arg2 (r4) : passed in r22 + */ + +ENTRY(_kernel_thread_helper) + l.or r3,r20,r0 + l.or r4,r22,r0 + l.movhi r31,hi(kernel_thread_helper) + l.ori r31,r31,lo(kernel_thread_helper) + l.jr r31 + l.nop + + /* ========================================================[ switch ] === */ /* @@ -1034,13 +1044,8 @@ ENTRY(_switch) /* Unwind stack to pre-switch state */ l.addi r1,r1,(INT_FRAME_SIZE) - /* Return via the link-register back to where we 'came from', where - * that 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 */ + /* Return via the link-register back to where we 'came from', where that can be + * either schedule() or return_from_fork()... */ l.jr r9 l.nop @@ -1083,6 +1088,10 @@ ENTRY(sys_fork) l.j _fork_save_extra_regs_and_call l.addi r3,r1,0 +ENTRY(sys_execve) + l.j _sys_execve + l.addi r6,r1,0 + ENTRY(sys_sigaltstack) l.j _sys_sigaltstack l.addi r5,r1,0 diff --git a/trunk/arch/openrisc/kernel/process.c b/trunk/arch/openrisc/kernel/process.c index e0874b8e09e4..c35f3ab1a8d3 100644 --- a/trunk/arch/openrisc/kernel/process.c +++ b/trunk/arch/openrisc/kernel/process.c @@ -109,82 +109,66 @@ void release_thread(struct task_struct *dead_task) */ extern asmlinkage void ret_from_fork(void); -/* - * copy_thread - * @clone_flags: flags - * @usp: user stack pointer or fn for kernel thread - * @arg: arg to fn for kernel thread; always NULL for userspace thread - * @p: the newly created task - * @regs: CPU context to copy for userspace thread; always NULL for kthread - * - * At the top of a newly initialized kernel stack are two stacked pt_reg - * structures. The first (topmost) is the userspace context of the thread. - * The second is the kernelspace context of the thread. - * - * A kernel thread will not be returning to userspace, so the topmost pt_regs - * struct can be uninitialized; it _does_ need to exist, though, because - * a kernel thread can become a userspace thread by doing a kernel_execve, in - * which case the topmost context will be initialized and used for 'returning' - * to userspace. - * - * The second pt_reg struct needs to be initialized to 'return' to - * ret_from_fork. A kernel thread will need to set r20 to the address of - * a function to call into (with arg in r22); userspace threads need to set - * r20 to NULL in which case ret_from_fork will just continue a return to - * userspace. - * - * A kernel thread 'fn' may return; this is effectively what happens when - * kernel_execve is called. In that case, the userspace pt_regs must have - * been initialized (which kernel_execve takes care of, see start_thread - * below); ret_from_fork will then continue its execution causing the - * 'kernel thread' to return to userspace as a userspace thread. - */ - int copy_thread(unsigned long clone_flags, unsigned long usp, - unsigned long arg, struct task_struct *p, struct pt_regs *regs) + unsigned long unused, struct task_struct *p, struct pt_regs *regs) { - struct pt_regs *userregs; + struct pt_regs *childregs; struct pt_regs *kregs; unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE; + struct thread_info *ti; unsigned long top_of_kernel_stack; top_of_kernel_stack = sp; p->set_child_tid = p->clear_child_tid = NULL; - /* Locate userspace context on stack... */ - sp -= STACK_FRAME_OVERHEAD; /* redzone */ + /* Copy registers */ + /* redzone */ + sp -= STACK_FRAME_OVERHEAD; sp -= sizeof(struct pt_regs); - userregs = (struct pt_regs *) sp; + childregs = (struct pt_regs *)sp; - /* ...and kernel context */ - sp -= STACK_FRAME_OVERHEAD; /* redzone */ - sp -= sizeof(struct pt_regs); - kregs = (struct pt_regs *)sp; + /* Copy parent registers */ + *childregs = *regs; - if (unlikely(p->flags & PF_KTHREAD)) { - memset(kregs, 0, sizeof(struct pt_regs)); - kregs->gpr[20] = usp; /* fn, kernel thread */ - kregs->gpr[22] = arg; + if ((childregs->sr & SPR_SR_SM) == 1) { + /* for kernel thread, set `current_thread_info' + * and stackptr in new task + */ + childregs->sp = (unsigned long)task_stack_page(p) + THREAD_SIZE; + childregs->gpr[10] = (unsigned long)task_thread_info(p); } else { - *userregs = *regs; - - userregs->sp = usp; - userregs->gpr[11] = 0; /* Result from fork() */ - - kregs->gpr[20] = 0; /* Userspace thread */ + childregs->sp = usp; } + childregs->gpr[11] = 0; /* Result from fork() */ + /* - * _switch wants the kernel stack page in pt_regs->sp so that it - * can restore it to thread_info->ksp... see _switch for details. + * 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 + * the 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; - task_thread_info(p)->ksp = (unsigned long)kregs; - return 0; } @@ -193,14 +177,16 @@ copy_thread(unsigned long clone_flags, unsigned long usp, */ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) { - unsigned long sr = mfspr(SPR_SR) & ~SPR_SR_SM; + unsigned long sr = regs->sr & ~SPR_SR_SM; set_fs(USER_DS); - memset(regs, 0, sizeof(struct pt_regs)); + memset(regs->gpr, 0, sizeof(regs->gpr)); regs->pc = pc; regs->sr = sr; regs->sp = sp; + +/* printk("start thread, ksp = %lx\n", current_thread_info()->ksp);*/ } /* Fill in the fpu structure for a core dump. */ @@ -251,9 +237,74 @@ void dump_elf_thread(elf_greg_t *dest, struct pt_regs* regs) dest[35] = 0; } +extern void _kernel_thread_helper(void); + +void __noreturn kernel_thread_helper(int (*fn) (void *), void *arg) +{ + do_exit(fn(arg)); +} + +/* + * Create a kernel thread. + */ +int kernel_thread(int (*fn) (void *), void *arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + + regs.gpr[20] = (unsigned long)fn; + regs.gpr[22] = (unsigned long)arg; + regs.sr = mfspr(SPR_SR); + regs.pc = (unsigned long)_kernel_thread_helper; + + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, + 0, ®s, 0, NULL, NULL); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage long _sys_execve(const char __user *name, + const char __user * const __user *argv, + const char __user * const __user *envp, + struct pt_regs *regs) +{ + int error; + struct filename *filename; + + filename = getname(name); + error = PTR_ERR(filename); + + if (IS_ERR(filename)) + goto out; + + error = do_execve(filename->name, argv, envp, regs); + putname(filename); + +out: + return error; +} + unsigned long get_wchan(struct task_struct *p) { /* TODO */ return 0; } + +int kernel_execve(const char *filename, char *const argv[], char *const envp[]) +{ + register long __res asm("r11") = __NR_execve; + register long __a asm("r3") = (long)(filename); + register long __b asm("r4") = (long)(argv); + register long __c asm("r5") = (long)(envp); + __asm__ volatile ("l.sys 1" + : "=r" (__res), "=r"(__a), "=r"(__b), "=r"(__c) + : "0"(__res), "1"(__a), "2"(__b), "3"(__c) + : "r6", "r7", "r8", "r12", "r13", "r15", + "r17", "r19", "r21", "r23", "r25", "r27", + "r29", "r31"); + __asm__ volatile ("l.nop"); + return __res; +} diff --git a/trunk/arch/parisc/Kconfig b/trunk/arch/parisc/Kconfig index 11def45b98c5..c779da7cd96b 100644 --- a/trunk/arch/parisc/Kconfig +++ b/trunk/arch/parisc/Kconfig @@ -22,6 +22,7 @@ config PARISC select GENERIC_STRNCPY_FROM_USER select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA + select GENERIC_KERNEL_THREAD help The PA-RISC microprocessor is designed by Hewlett-Packard and used diff --git a/trunk/arch/parisc/kernel/entry.S b/trunk/arch/parisc/kernel/entry.S index 18670a078849..18d05e7373b5 100644 --- a/trunk/arch/parisc/kernel/entry.S +++ b/trunk/arch/parisc/kernel/entry.S @@ -707,60 +707,10 @@ ENTRY(end_fault_vector) .import handle_interruption,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 */ -#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 * - * copy_thread moved args from temp save area set up above - * into task save area. + * copy_thread moved args into task save area. */ ENTRY(ret_from_kernel_thread) @@ -773,7 +723,6 @@ ENTRY(ret_from_kernel_thread) LDREG TASK_PT_GR25(%r1), %r26 #ifdef CONFIG_64BIT LDREG TASK_PT_GR27(%r1), %r27 - LDREG TASK_PT_GR22(%r1), %r22 #endif LDREG TASK_PT_GR26(%r1), %r1 ble 0(%sr7, %r1) diff --git a/trunk/arch/parisc/kernel/process.c b/trunk/arch/parisc/kernel/process.c index cbc37216bf90..b7b4126774e0 100644 --- a/trunk/arch/parisc/kernel/process.c +++ b/trunk/arch/parisc/kernel/process.c @@ -164,23 +164,6 @@ void machine_power_off(void) void (*pm_power_off)(void) = machine_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.. */ @@ -256,8 +239,8 @@ sys_vfork(struct pt_regs *regs) int copy_thread(unsigned long clone_flags, unsigned long usp, - unsigned long unused, /* in ia64 this is "user_stack_size" */ - struct task_struct * p, struct pt_regs * pregs) + unsigned long arg, + struct task_struct *p, struct pt_regs *pregs) { struct pt_regs * cregs = &(p->thread.regs); void *stack = task_stack_page(p); @@ -271,21 +254,8 @@ copy_thread(unsigned long clone_flags, unsigned long usp, extern void * const hpux_child_return; #endif - *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) { + if (unlikely((p->flags & PF_KTHREAD) && usp != 0)) { + memset(cregs, 0, sizeof(struct pt_regs)); /* kernel thread */ cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN; /* Must exit via ret_from_kernel_thread in order @@ -297,10 +267,12 @@ copy_thread(unsigned long clone_flags, unsigned long usp, * ret_from_kernel_thread. */ #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 - cregs->gr[26] = pregs->gr[26]; - cregs->gr[25] = pregs->gr[25]; + cregs->gr[25] = arg; } else { /* user thread */ /* @@ -308,6 +280,13 @@ copy_thread(unsigned long clone_flags, unsigned long usp, * for setting gr[21]. */ + *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 */ + /* Use same stack depth as parent */ cregs->ksp = (unsigned long)stack + (pregs->gr[21] & (THREAD_SIZE - 1)); diff --git a/trunk/arch/sparc/Kconfig b/trunk/arch/sparc/Kconfig index e52f3c2ad3dd..b6b442b0d793 100644 --- a/trunk/arch/sparc/Kconfig +++ b/trunk/arch/sparc/Kconfig @@ -40,8 +40,6 @@ config SPARC select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER select MODULES_USE_ELF_RELA - select GENERIC_KERNEL_THREAD - select GENERIC_KERNEL_EXECVE config SPARC32 def_bool !64BIT diff --git a/trunk/arch/sparc/include/asm/processor_32.h b/trunk/arch/sparc/include/asm/processor_32.h index c1e01914fd98..f74ac9ee33a8 100644 --- a/trunk/arch/sparc/include/asm/processor_32.h +++ b/trunk/arch/sparc/include/asm/processor_32.h @@ -106,6 +106,7 @@ static inline void start_thread(struct pt_regs * regs, unsigned long pc, /* Free all resources held by a thread. */ #define release_thread(tsk) do { } while(0) +extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); extern unsigned long get_wchan(struct task_struct *); diff --git a/trunk/arch/sparc/include/asm/processor_64.h b/trunk/arch/sparc/include/asm/processor_64.h index 0305d56d9b1a..4e5a483122a0 100644 --- a/trunk/arch/sparc/include/asm/processor_64.h +++ b/trunk/arch/sparc/include/asm/processor_64.h @@ -94,7 +94,6 @@ struct thread_struct { #ifndef __ASSEMBLY__ #include -#include /* Return saved PC of a blocked thread. */ struct task_struct; @@ -144,10 +143,6 @@ do { \ : \ : "r" (regs), "r" (sp - sizeof(struct reg_window) - STACK_BIAS), \ "i" ((const unsigned long)(&((struct pt_regs *)0)->u_regs[0]))); \ - fprs_write(0); \ - current_thread_info()->xfsr[0] = 0; \ - current_thread_info()->fpsaved[0] = 0; \ - regs->tstate &= ~TSTATE_PEF; \ } while (0) #define start_thread32(regs, pc, sp) \ @@ -188,15 +183,13 @@ do { \ : \ : "r" (regs), "r" (sp - sizeof(struct reg_window32)), \ "i" ((const unsigned long)(&((struct pt_regs *)0)->u_regs[0]))); \ - fprs_write(0); \ - current_thread_info()->xfsr[0] = 0; \ - current_thread_info()->fpsaved[0] = 0; \ - regs->tstate &= ~TSTATE_PEF; \ } while (0) /* Free all resources held by a thread. */ #define release_thread(tsk) do { } while (0) +extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); + extern unsigned long get_wchan(struct task_struct *task); #define task_pt_regs(tsk) (task_thread_info(tsk)->kregs) diff --git a/trunk/arch/sparc/include/asm/ptrace.h b/trunk/arch/sparc/include/asm/ptrace.h index 1e8b81802263..0c6f6b068289 100644 --- a/trunk/arch/sparc/include/asm/ptrace.h +++ b/trunk/arch/sparc/include/asm/ptrace.h @@ -32,9 +32,6 @@ static inline bool pt_regs_clear_syscall(struct pt_regs *regs) #define arch_ptrace_stop(exit_code, info) \ synchronize_user_stack() -#define current_pt_regs() \ - ((struct pt_regs *)((unsigned long)current_thread_info() + THREAD_SIZE) - 1) - struct global_reg_snapshot { unsigned long tstate; unsigned long tpc; @@ -47,7 +44,9 @@ struct global_reg_snapshot { }; extern struct global_reg_snapshot global_reg_snapshot[NR_CPUS]; -#define force_successful_syscall_return() set_thread_noerror(1) +#define force_successful_syscall_return() \ +do { current_thread_info()->syscall_noerror = 1; \ +} while (0) #define user_mode(regs) (!((regs)->tstate & TSTATE_PRIV)) #define instruction_pointer(regs) ((regs)->tpc) #define instruction_pointer_set(regs, val) ((regs)->tpc = (val)) @@ -90,9 +89,6 @@ static inline bool pt_regs_clear_syscall(struct pt_regs *regs) #define arch_ptrace_stop(exit_code, info) \ synchronize_user_stack() -#define current_pt_regs() \ - ((struct pt_regs *)((unsigned long)current_thread_info() + THREAD_SIZE) - 1) - #define user_mode(regs) (!((regs)->psr & PSR_PS)) #define instruction_pointer(regs) ((regs)->pc) #define user_stack_pointer(regs) ((regs)->u_regs[UREG_FP]) diff --git a/trunk/arch/sparc/include/asm/switch_to_64.h b/trunk/arch/sparc/include/asm/switch_to_64.h index cad36f56fa03..7923c4a2be38 100644 --- a/trunk/arch/sparc/include/asm/switch_to_64.h +++ b/trunk/arch/sparc/include/asm/switch_to_64.h @@ -23,7 +23,7 @@ do { flush_tlb_pending(); \ /* If you are tempted to conditionalize the following */ \ /* so that ASI is only written if it changes, think again. */ \ __asm__ __volatile__("wr %%g0, %0, %%asi" \ - : : "r" (task_thread_info(next)->current_ds));\ + : : "r" (__thread_flag_byte_ptr(task_thread_info(next))[TI_FLAG_BYTE_CURRENT_DS]));\ trap_block[current_thread_info()->cpu].thread = \ task_thread_info(next); \ __asm__ __volatile__( \ diff --git a/trunk/arch/sparc/include/asm/syscalls.h b/trunk/arch/sparc/include/asm/syscalls.h index bf8972adea17..45a43f637a14 100644 --- a/trunk/arch/sparc/include/asm/syscalls.h +++ b/trunk/arch/sparc/include/asm/syscalls.h @@ -8,4 +8,6 @@ extern asmlinkage long sparc_do_fork(unsigned long clone_flags, struct pt_regs *regs, unsigned long stack_size); +extern asmlinkage int sparc_execve(struct pt_regs *regs); + #endif /* _SPARC64_SYSCALLS_H */ diff --git a/trunk/arch/sparc/include/asm/thread_info_64.h b/trunk/arch/sparc/include/asm/thread_info_64.h index ea2ba22d595a..4e2276631081 100644 --- a/trunk/arch/sparc/include/asm/thread_info_64.h +++ b/trunk/arch/sparc/include/asm/thread_info_64.h @@ -14,12 +14,12 @@ #define TI_FLAG_FAULT_CODE_SHIFT 56 #define TI_FLAG_BYTE_WSTATE 1 #define TI_FLAG_WSTATE_SHIFT 48 -#define TI_FLAG_BYTE_NOERROR 2 -#define TI_FLAG_BYTE_NOERROR_SHIFT 40 -#define TI_FLAG_BYTE_FPDEPTH 3 -#define TI_FLAG_FPDEPTH_SHIFT 32 -#define TI_FLAG_BYTE_CWP 4 -#define TI_FLAG_CWP_SHIFT 24 +#define TI_FLAG_BYTE_CWP 2 +#define TI_FLAG_CWP_SHIFT 40 +#define TI_FLAG_BYTE_CURRENT_DS 3 +#define TI_FLAG_CURRENT_DS_SHIFT 32 +#define TI_FLAG_BYTE_FPDEPTH 4 +#define TI_FLAG_FPDEPTH_SHIFT 24 #define TI_FLAG_BYTE_WSAVED 5 #define TI_FLAG_WSAVED_SHIFT 16 @@ -47,7 +47,7 @@ struct thread_info { struct exec_domain *exec_domain; int preempt_count; /* 0 => preemptable, <0 => BUG */ __u8 new_child; - __u8 current_ds; + __u8 syscall_noerror; __u16 cpu; unsigned long *utraps; @@ -74,9 +74,9 @@ struct thread_info { #define TI_FAULT_CODE (TI_FLAGS + TI_FLAG_BYTE_FAULT_CODE) #define TI_WSTATE (TI_FLAGS + TI_FLAG_BYTE_WSTATE) #define TI_CWP (TI_FLAGS + TI_FLAG_BYTE_CWP) +#define TI_CURRENT_DS (TI_FLAGS + TI_FLAG_BYTE_CURRENT_DS) #define TI_FPDEPTH (TI_FLAGS + TI_FLAG_BYTE_FPDEPTH) #define TI_WSAVED (TI_FLAGS + TI_FLAG_BYTE_WSAVED) -#define TI_SYS_NOERROR (TI_FLAGS + TI_FLAG_BYTE_NOERROR) #define TI_FPSAVED 0x00000010 #define TI_KSP 0x00000018 #define TI_FAULT_ADDR 0x00000020 @@ -84,7 +84,7 @@ struct thread_info { #define TI_EXEC_DOMAIN 0x00000030 #define TI_PRE_COUNT 0x00000038 #define TI_NEW_CHILD 0x0000003c -#define TI_CURRENT_DS 0x0000003d +#define TI_SYS_NOERROR 0x0000003d #define TI_CPU 0x0000003e #define TI_UTRAPS 0x00000040 #define TI_REG_WINDOW 0x00000048 @@ -121,7 +121,7 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .current_ds = ASI_P, \ + .flags = ((unsigned long)ASI_P) << TI_FLAG_CURRENT_DS_SHIFT, \ .exec_domain = &default_exec_domain, \ .preempt_count = INIT_PREEMPT_COUNT, \ .restart_block = { \ @@ -153,12 +153,13 @@ register struct thread_info *current_thread_info_reg asm("g6"); #define set_thread_wstate(val) (__cur_thread_flag_byte_ptr[TI_FLAG_BYTE_WSTATE] = (val)) #define get_thread_cwp() (__cur_thread_flag_byte_ptr[TI_FLAG_BYTE_CWP]) #define set_thread_cwp(val) (__cur_thread_flag_byte_ptr[TI_FLAG_BYTE_CWP] = (val)) -#define get_thread_noerror() (__cur_thread_flag_byte_ptr[TI_FLAG_BYTE_NOERROR]) -#define set_thread_noerror(val) (__cur_thread_flag_byte_ptr[TI_FLAG_BYTE_NOERROR] = (val)) +#define get_thread_current_ds() (__cur_thread_flag_byte_ptr[TI_FLAG_BYTE_CURRENT_DS]) +#define set_thread_current_ds(val) (__cur_thread_flag_byte_ptr[TI_FLAG_BYTE_CURRENT_DS] = (val)) #define get_thread_fpdepth() (__cur_thread_flag_byte_ptr[TI_FLAG_BYTE_FPDEPTH]) #define set_thread_fpdepth(val) (__cur_thread_flag_byte_ptr[TI_FLAG_BYTE_FPDEPTH] = (val)) #define get_thread_wsaved() (__cur_thread_flag_byte_ptr[TI_FLAG_BYTE_WSAVED]) #define set_thread_wsaved(val) (__cur_thread_flag_byte_ptr[TI_FLAG_BYTE_WSAVED] = (val)) + #endif /* !(__ASSEMBLY__) */ /* diff --git a/trunk/arch/sparc/include/asm/uaccess_64.h b/trunk/arch/sparc/include/asm/uaccess_64.h index e562d3caee57..73083e1d38d9 100644 --- a/trunk/arch/sparc/include/asm/uaccess_64.h +++ b/trunk/arch/sparc/include/asm/uaccess_64.h @@ -38,14 +38,14 @@ #define VERIFY_READ 0 #define VERIFY_WRITE 1 -#define get_fs() ((mm_segment_t){(current_thread_info()->current_ds)}) +#define get_fs() ((mm_segment_t) { get_thread_current_ds() }) #define get_ds() (KERNEL_DS) #define segment_eq(a,b) ((a).seg == (b).seg) #define set_fs(val) \ do { \ - current_thread_info()->current_ds =(val).seg; \ + set_thread_current_ds((val).seg); \ __asm__ __volatile__ ("wr %%g0, %0, %%asi" : : "r" ((val).seg)); \ } while(0) diff --git a/trunk/arch/sparc/include/asm/unistd.h b/trunk/arch/sparc/include/asm/unistd.h index c3e5d8b64171..0ecea6ed943e 100644 --- a/trunk/arch/sparc/include/asm/unistd.h +++ b/trunk/arch/sparc/include/asm/unistd.h @@ -46,7 +46,6 @@ #define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_COMPAT_SYS_SENDFILE #endif -#define __ARCH_WANT_SYS_EXECVE /* * "Conditional" syscalls diff --git a/trunk/arch/sparc/kernel/entry.S b/trunk/arch/sparc/kernel/entry.S index 21fd1a8f47d2..dcaa1cf0de40 100644 --- a/trunk/arch/sparc/kernel/entry.S +++ b/trunk/arch/sparc/kernel/entry.S @@ -806,10 +806,23 @@ sys_nis_syscall: call c_sys_nis_syscall mov %l5, %o7 -sunos_execv: + .align 4 + .globl sys_execve +sys_execve: + mov %o7, %l5 + add %sp, STACKFRAME_SZ, %o0 ! pt_regs *regs arg + call sparc_execve + mov %l5, %o7 + .globl sunos_execv - b sys_execve - clr %i2 +sunos_execv: + st %g0, [%sp + STACKFRAME_SZ + PT_I2] + + call sparc_execve + add %sp, STACKFRAME_SZ, %o0 + + b ret_sys_call + ld [%sp + STACKFRAME_SZ + PT_I0], %o0 .align 4 .globl sys_sparc_pipe @@ -946,9 +959,17 @@ flush_patch_four: .align 4 linux_sparc_ni_syscall: sethi %hi(sys_ni_syscall), %l7 - b do_syscall + b syscall_is_too_hard or %l7, %lo(sys_ni_syscall), %l7 +linux_fast_syscall: + andn %l7, 3, %l7 + mov %i0, %o0 + mov %i1, %o1 + mov %i2, %o2 + jmpl %l7 + %g0, %g0 + mov %i3, %o3 + linux_syscall_trace: add %sp, STACKFRAME_SZ, %o0 call syscall_trace @@ -970,23 +991,6 @@ ret_from_fork: b ret_sys_call ld [%sp + STACKFRAME_SZ + PT_I0], %o0 - .globl ret_from_kernel_thread -ret_from_kernel_thread: - call schedule_tail - ld [%g3 + TI_TASK], %o0 - ld [%sp + STACKFRAME_SZ + PT_G1], %l0 - call %l0 - ld [%sp + STACKFRAME_SZ + PT_G2], %o0 - rd %psr, %l1 - ld [%sp + STACKFRAME_SZ + PT_PSR], %l0 - andn %l0, PSR_CWP, %l0 - nop - and %l1, PSR_CWP, %l1 - or %l0, %l1, %l0 - st %l0, [%sp + STACKFRAME_SZ + PT_PSR] - b ret_sys_call - mov 0, %o0 - /* Linux native system calls enter here... */ .align 4 .globl linux_sparc_syscall @@ -998,8 +1002,11 @@ linux_sparc_syscall: bgeu linux_sparc_ni_syscall sll %g1, 2, %l4 ld [%l7 + %l4], %l7 + andcc %l7, 1, %g0 + bne linux_fast_syscall + /* Just do first insn from SAVE_ALL in the delay slot */ -do_syscall: +syscall_is_too_hard: SAVE_ALL_HEAD rd %wim, %l3 diff --git a/trunk/arch/sparc/kernel/etrap_64.S b/trunk/arch/sparc/kernel/etrap_64.S index 1276ca2567ba..786b185e6e3f 100644 --- a/trunk/arch/sparc/kernel/etrap_64.S +++ b/trunk/arch/sparc/kernel/etrap_64.S @@ -92,10 +92,8 @@ etrap_save: save %g2, -STACK_BIAS, %sp rdpr %wstate, %g2 wrpr %g0, 0, %canrestore sll %g2, 3, %g2 - - /* Set TI_SYS_FPDEPTH to 1 and clear TI_SYS_NOERROR. */ mov 1, %l5 - sth %l5, [%l6 + TI_SYS_NOERROR] + stb %l5, [%l6 + TI_FPDEPTH] wrpr %g3, 0, %otherwin wrpr %g2, 0, %wstate @@ -154,9 +152,7 @@ etrap_save: save %g2, -STACK_BIAS, %sp add %l6, TI_FPSAVED + 1, %l4 srl %l5, 1, %l3 add %l5, 2, %l5 - - /* Set TI_SYS_FPDEPTH to %l5 and clear TI_SYS_NOERROR. */ - sth %l5, [%l6 + TI_SYS_NOERROR] + stb %l5, [%l6 + TI_FPDEPTH] ba,pt %xcc, 2b stb %g0, [%l4 + %l3] nop diff --git a/trunk/arch/sparc/kernel/process_32.c b/trunk/arch/sparc/kernel/process_32.c index bf4c6addce7b..487bffb36f5e 100644 --- a/trunk/arch/sparc/kernel/process_32.c +++ b/trunk/arch/sparc/kernel/process_32.c @@ -316,10 +316,9 @@ asmlinkage int sparc_do_fork(unsigned long clone_flags, * XXX See comment above sys_vfork in sparc64. todo. */ extern void ret_from_fork(void); -extern void ret_from_kernel_thread(void); int copy_thread(unsigned long clone_flags, unsigned long sp, - unsigned long arg, + unsigned long unused, struct task_struct *p, struct pt_regs *regs) { struct thread_info *ti = task_thread_info(p); @@ -337,13 +336,16 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, } /* - * p->thread_info new_stack childregs stack bottom - * ! ! ! ! - * V V (stk.fr.) V (pt_regs) V - * +----- - - - - - ------+===========+=============+ + * p->thread_info new_stack childregs + * ! ! ! {if(PSR_PS) } + * V V (stk.fr.) V (pt_regs) { (stk.fr.) } + * +----- - - - - - ------+===========+============={+==========}+ */ new_stack = task_stack_page(p) + THREAD_SIZE; + if (regs->psr & PSR_PS) + new_stack -= STACKFRAME_SZ; new_stack -= STACKFRAME_SZ + TRACEREG_SZ; + memcpy(new_stack, (char *)regs - STACKFRAME_SZ, STACKFRAME_SZ + TRACEREG_SZ); childregs = (struct pt_regs *) (new_stack + STACKFRAME_SZ); /* @@ -354,58 +356,55 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, * Thus, kpsr|=PSR_PIL. */ ti->ksp = (unsigned long) new_stack; - p->thread.kregs = childregs; - - if (unlikely(p->flags & PF_KTHREAD)) { - extern int nwindows; - unsigned long psr; - memset(new_stack, 0, STACKFRAME_SZ + TRACEREG_SZ); - p->thread.flags |= SPARC_FLAG_KTHREAD; - p->thread.current_ds = KERNEL_DS; - ti->kpc = (((unsigned long) ret_from_kernel_thread) - 0x8); - childregs->u_regs[UREG_G1] = sp; /* function */ - childregs->u_regs[UREG_G2] = arg; - psr = childregs->psr = get_psr(); - ti->kpsr = psr | PSR_PIL; - ti->kwim = 1 << (((psr & PSR_CWP) + 1) % nwindows); - return 0; - } - memcpy(new_stack, (char *)regs - STACKFRAME_SZ, STACKFRAME_SZ + TRACEREG_SZ); - childregs->u_regs[UREG_FP] = sp; - p->thread.flags &= ~SPARC_FLAG_KTHREAD; - p->thread.current_ds = USER_DS; ti->kpc = (((unsigned long) ret_from_fork) - 0x8); ti->kpsr = current->thread.fork_kpsr | PSR_PIL; ti->kwim = current->thread.fork_kwim; - if (sp != regs->u_regs[UREG_FP]) { - struct sparc_stackf __user *childstack; - struct sparc_stackf __user *parentstack; + if(regs->psr & PSR_PS) { + extern struct pt_regs fake_swapper_regs; - /* - * This is a clone() call with supplied user stack. - * Set some valid stack frames to give to the child. - */ - childstack = (struct sparc_stackf __user *) - (sp & ~0xfUL); - parentstack = (struct sparc_stackf __user *) - regs->u_regs[UREG_FP]; + p->thread.kregs = &fake_swapper_regs; + new_stack += STACKFRAME_SZ + TRACEREG_SZ; + childregs->u_regs[UREG_FP] = (unsigned long) new_stack; + p->thread.flags |= SPARC_FLAG_KTHREAD; + p->thread.current_ds = KERNEL_DS; + memcpy(new_stack, (void *)regs->u_regs[UREG_FP], STACKFRAME_SZ); + childregs->u_regs[UREG_G6] = (unsigned long) ti; + } else { + p->thread.kregs = childregs; + childregs->u_regs[UREG_FP] = sp; + p->thread.flags &= ~SPARC_FLAG_KTHREAD; + p->thread.current_ds = USER_DS; + + if (sp != regs->u_regs[UREG_FP]) { + struct sparc_stackf __user *childstack; + struct sparc_stackf __user *parentstack; + + /* + * This is a clone() call with supplied user stack. + * Set some valid stack frames to give to the child. + */ + childstack = (struct sparc_stackf __user *) + (sp & ~0xfUL); + parentstack = (struct sparc_stackf __user *) + regs->u_regs[UREG_FP]; #if 0 - printk("clone: parent stack:\n"); - show_stackframe(parentstack); + printk("clone: parent stack:\n"); + show_stackframe(parentstack); #endif - childstack = clone_stackframe(childstack, parentstack); - if (!childstack) - return -EFAULT; + childstack = clone_stackframe(childstack, parentstack); + if (!childstack) + return -EFAULT; #if 0 - printk("clone: child stack:\n"); - show_stackframe(childstack); + printk("clone: child stack:\n"); + show_stackframe(childstack); #endif - childregs->u_regs[UREG_FP] = (unsigned long)childstack; + childregs->u_regs[UREG_FP] = (unsigned long)childstack; + } } #ifdef CONFIG_SMP @@ -476,6 +475,69 @@ int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs) return 1; } +/* + * sparc_execve() executes a new program after the asm stub has set + * things up for us. This should basically do what I want it to. + */ +asmlinkage int sparc_execve(struct pt_regs *regs) +{ + int error, base = 0; + struct filename *filename; + + /* Check for indirect call. */ + if(regs->u_regs[UREG_G1] == 0) + base = 1; + + filename = getname((char __user *)regs->u_regs[base + UREG_I0]); + error = PTR_ERR(filename); + if(IS_ERR(filename)) + goto out; + error = do_execve(filename->name, + (const char __user *const __user *) + regs->u_regs[base + UREG_I1], + (const char __user *const __user *) + regs->u_regs[base + UREG_I2], + regs); + putname(filename); +out: + return error; +} + +/* + * 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 freed until both the parent and the child have exited. + */ +pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + long retval; + + __asm__ __volatile__("mov %4, %%g2\n\t" /* Set aside fn ptr... */ + "mov %5, %%g3\n\t" /* and arg. */ + "mov %1, %%g1\n\t" + "mov %2, %%o0\n\t" /* Clone flags. */ + "mov 0, %%o1\n\t" /* usp arg == 0 */ + "t 0x10\n\t" /* Linux/Sparc clone(). */ + "cmp %%o1, 0\n\t" + "be 1f\n\t" /* The parent, just return. */ + " nop\n\t" /* Delay slot. */ + "jmpl %%g2, %%o7\n\t" /* Call the function. */ + " mov %%g3, %%o0\n\t" /* Get back the arg in delay. */ + "mov %3, %%g1\n\t" + "t 0x10\n\t" /* Linux/Sparc exit(). */ + /* Notreached by child. */ + "1: mov %%o0, %0\n\t" : + "=r" (retval) : + "i" (__NR_clone), "r" (flags | CLONE_VM | CLONE_UNTRACED), + "i" (__NR_exit), "r" (fn), "r" (arg) : + "g1", "g2", "g3", "o0", "o1", "memory", "cc"); + return retval; +} +EXPORT_SYMBOL(kernel_thread); + unsigned long get_wchan(struct task_struct *task) { unsigned long pc, fp, bias = 0; diff --git a/trunk/arch/sparc/kernel/process_64.c b/trunk/arch/sparc/kernel/process_64.c index 6b36e879b2a0..fcaa59421126 100644 --- a/trunk/arch/sparc/kernel/process_64.c +++ b/trunk/arch/sparc/kernel/process_64.c @@ -538,55 +538,64 @@ asmlinkage long sparc_do_fork(unsigned long clone_flags, * Child --> %o0 == parents pid, %o1 == 1 */ int copy_thread(unsigned long clone_flags, unsigned long sp, - unsigned long arg, + unsigned long unused, struct task_struct *p, struct pt_regs *regs) { struct thread_info *t = task_thread_info(p); struct sparc_stackf *parent_sf; unsigned long child_stack_sz; char *child_trap_frame; + int kernel_thread; + + kernel_thread = (regs->tstate & TSTATE_PRIV) ? 1 : 0; + parent_sf = ((struct sparc_stackf *) regs) - 1; /* Calculate offset to stack_frame & pt_regs */ - child_stack_sz = (STACKFRAME_SZ + TRACEREG_SZ); + child_stack_sz = ((STACKFRAME_SZ + TRACEREG_SZ) + + (kernel_thread ? STACKFRAME_SZ : 0)); child_trap_frame = (task_stack_page(p) + (THREAD_SIZE - child_stack_sz)); + memcpy(child_trap_frame, parent_sf, child_stack_sz); + t->flags = (t->flags & ~((0xffUL << TI_FLAG_CWP_SHIFT) | + (0xffUL << TI_FLAG_CURRENT_DS_SHIFT))) | + (((regs->tstate + 1) & TSTATE_CWP) << TI_FLAG_CWP_SHIFT); t->new_child = 1; t->ksp = ((unsigned long) child_trap_frame) - STACK_BIAS; t->kregs = (struct pt_regs *) (child_trap_frame + sizeof(struct sparc_stackf)); t->fpsaved[0] = 0; - if (unlikely(p->flags & PF_KTHREAD)) { - memset(child_trap_frame, 0, child_stack_sz); - __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_CWP] = - (current_pt_regs()->tstate + 1) & TSTATE_CWP; - t->current_ds = ASI_P; - t->kregs->u_regs[UREG_G1] = sp; /* function */ - t->kregs->u_regs[UREG_G2] = arg; - return 0; - } + if (kernel_thread) { + struct sparc_stackf *child_sf = (struct sparc_stackf *) + (child_trap_frame + (STACKFRAME_SZ + TRACEREG_SZ)); - parent_sf = ((struct sparc_stackf *) regs) - 1; - memcpy(child_trap_frame, parent_sf, child_stack_sz); - if (t->flags & _TIF_32BIT) { - sp &= 0x00000000ffffffffUL; - regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; - } - t->kregs->u_regs[UREG_FP] = sp; - __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_CWP] = - (regs->tstate + 1) & TSTATE_CWP; - t->current_ds = ASI_AIUS; - if (sp != regs->u_regs[UREG_FP]) { - unsigned long csp; - - csp = clone_stackframe(sp, regs->u_regs[UREG_FP]); - if (!csp) - return -EFAULT; - t->kregs->u_regs[UREG_FP] = csp; + /* Zero terminate the stack backtrace. */ + child_sf->fp = NULL; + t->kregs->u_regs[UREG_FP] = + ((unsigned long) child_sf) - STACK_BIAS; + + t->flags |= ((long)ASI_P << TI_FLAG_CURRENT_DS_SHIFT); + t->kregs->u_regs[UREG_G6] = (unsigned long) t; + t->kregs->u_regs[UREG_G4] = (unsigned long) t->task; + } else { + if (t->flags & _TIF_32BIT) { + sp &= 0x00000000ffffffffUL; + regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; + } + t->kregs->u_regs[UREG_FP] = sp; + t->flags |= ((long)ASI_AIUS << TI_FLAG_CURRENT_DS_SHIFT); + if (sp != regs->u_regs[UREG_FP]) { + unsigned long csp; + + csp = clone_stackframe(sp, regs->u_regs[UREG_FP]); + if (!csp) + return -EFAULT; + t->kregs->u_regs[UREG_FP] = csp; + } + if (t->utraps) + t->utraps[0]++; } - if (t->utraps) - t->utraps[0]++; /* Set the return value for the child. */ t->kregs->u_regs[UREG_I0] = current->pid; @@ -601,6 +610,45 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, return 0; } +/* + * 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 freed until both the parent and the child have exited. + */ +pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + long retval; + + /* If the parent runs before fn(arg) is called by the child, + * the input registers of this function can be clobbered. + * So we stash 'fn' and 'arg' into global registers which + * will not be modified by the parent. + */ + __asm__ __volatile__("mov %4, %%g2\n\t" /* Save FN into global */ + "mov %5, %%g3\n\t" /* Save ARG into global */ + "mov %1, %%g1\n\t" /* Clone syscall nr. */ + "mov %2, %%o0\n\t" /* Clone flags. */ + "mov 0, %%o1\n\t" /* usp arg == 0 */ + "t 0x6d\n\t" /* Linux/Sparc clone(). */ + "brz,a,pn %%o1, 1f\n\t" /* Parent, just return. */ + " mov %%o0, %0\n\t" + "jmpl %%g2, %%o7\n\t" /* Call the function. */ + " mov %%g3, %%o0\n\t" /* Set arg in delay. */ + "mov %3, %%g1\n\t" + "t 0x6d\n\t" /* Linux/Sparc exit(). */ + /* Notreached by child. */ + "1:" : + "=r" (retval) : + "i" (__NR_clone), "r" (flags | CLONE_VM | CLONE_UNTRACED), + "i" (__NR_exit), "r" (fn), "r" (arg) : + "g1", "g2", "g3", "o0", "o1", "memory", "cc"); + return retval; +} +EXPORT_SYMBOL(kernel_thread); + typedef struct { union { unsigned int pr_regs[32]; @@ -667,6 +715,41 @@ int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs) } EXPORT_SYMBOL(dump_fpu); +/* + * sparc_execve() executes a new program after the asm stub has set + * things up for us. This should basically do what I want it to. + */ +asmlinkage int sparc_execve(struct pt_regs *regs) +{ + int error, base = 0; + struct filename *filename; + + /* User register window flush is done by entry.S */ + + /* Check for indirect call. */ + if (regs->u_regs[UREG_G1] == 0) + base = 1; + + filename = getname((char __user *)regs->u_regs[base + UREG_I0]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename->name, + (const char __user *const __user *) + regs->u_regs[base + UREG_I1], + (const char __user *const __user *) + regs->u_regs[base + UREG_I2], regs); + putname(filename); + if (!error) { + fprs_write(0); + current_thread_info()->xfsr[0] = 0; + current_thread_info()->fpsaved[0] = 0; + regs->tstate &= ~TSTATE_PEF; + } +out: + return error; +} + unsigned long get_wchan(struct task_struct *task) { unsigned long pc, fp, bias = 0; diff --git a/trunk/arch/sparc/kernel/sys_sparc32.c b/trunk/arch/sparc/kernel/sys_sparc32.c index 03c7e929ec34..c3239811a1b5 100644 --- a/trunk/arch/sparc/kernel/sys_sparc32.c +++ b/trunk/arch/sparc/kernel/sys_sparc32.c @@ -396,6 +396,42 @@ asmlinkage long compat_sys_rt_sigaction(int sig, return ret; } +/* + * sparc32_execve() executes a new program after the asm stub has set + * things up for us. This should basically do what I want it to. + */ +asmlinkage long sparc32_execve(struct pt_regs *regs) +{ + int error, base = 0; + struct filename *filename; + + /* User register window flush is done by entry.S */ + + /* Check for indirect call. */ + if ((u32)regs->u_regs[UREG_G1] == 0) + base = 1; + + filename = getname(compat_ptr(regs->u_regs[base + UREG_I0])); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + + error = compat_do_execve(filename->name, + compat_ptr(regs->u_regs[base + UREG_I1]), + compat_ptr(regs->u_regs[base + UREG_I2]), regs); + + putname(filename); + + if (!error) { + fprs_write(0); + current_thread_info()->xfsr[0] = 0; + current_thread_info()->fpsaved[0] = 0; + regs->tstate &= ~TSTATE_PEF; + } +out: + return error; +} + #ifdef CONFIG_MODULES asmlinkage long sys32_init_module(void __user *umod, u32 len, diff --git a/trunk/arch/sparc/kernel/sys_sparc_32.c b/trunk/arch/sparc/kernel/sys_sparc_32.c index a8e6eb0a11d5..0c9b31b22e07 100644 --- a/trunk/arch/sparc/kernel/sys_sparc_32.c +++ b/trunk/arch/sparc/kernel/sys_sparc_32.c @@ -258,3 +258,27 @@ asmlinkage int sys_getdomainname(char __user *name, int len) up_read(&uts_sem); return err; } + +/* + * 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[]) +{ + long __res; + register long __g1 __asm__ ("g1") = __NR_execve; + register long __o0 __asm__ ("o0") = (long)(filename); + register long __o1 __asm__ ("o1") = (long)(argv); + register long __o2 __asm__ ("o2") = (long)(envp); + asm volatile ("t 0x10\n\t" + "bcc 1f\n\t" + "mov %%o0, %0\n\t" + "sub %%g0, %%o0, %0\n\t" + "1:\n\t" + : "=r" (__res), "=&r" (__o0) + : "1" (__o0), "r" (__o1), "r" (__o2), "r" (__g1) + : "cc"); + return __res; +} diff --git a/trunk/arch/sparc/kernel/sys_sparc_64.c b/trunk/arch/sparc/kernel/sys_sparc_64.c index adfe60ece05f..11c6c9603e71 100644 --- a/trunk/arch/sparc/kernel/sys_sparc_64.c +++ b/trunk/arch/sparc/kernel/sys_sparc_64.c @@ -729,3 +729,25 @@ SYSCALL_DEFINE5(rt_sigaction, int, sig, const struct sigaction __user *, act, return ret; } + +/* + * 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[]) +{ + long __res; + register long __g1 __asm__ ("g1") = __NR_execve; + register long __o0 __asm__ ("o0") = (long)(filename); + register long __o1 __asm__ ("o1") = (long)(argv); + register long __o2 __asm__ ("o2") = (long)(envp); + asm volatile ("t 0x6d\n\t" + "sub %%g0, %%o0, %0\n\t" + "movcc %%xcc, %%o0, %0\n\t" + : "=r" (__res), "=&r" (__o0) + : "1" (__o0), "r" (__o1), "r" (__o2), "r" (__g1) + : "cc"); + return __res; +} diff --git a/trunk/arch/sparc/kernel/syscalls.S b/trunk/arch/sparc/kernel/syscalls.S index 2ef41e67f0be..7f5f65d0b3fd 100644 --- a/trunk/arch/sparc/kernel/syscalls.S +++ b/trunk/arch/sparc/kernel/syscalls.S @@ -1,20 +1,24 @@ /* SunOS's execv() call only specifies the argv argument, the * environment settings are the same as the calling processes. */ -sys64_execve: - set sys_execve, %g1 - jmpl %g1, %g0 - flushw +sys_execve: + sethi %hi(sparc_execve), %g1 + ba,pt %xcc, execve_merge + or %g1, %lo(sparc_execve), %g1 #ifdef CONFIG_COMPAT sunos_execv: - mov %g0, %o2 + stx %g0, [%sp + PTREGS_OFF + PT_V9_I2] sys32_execve: - set compat_sys_execve, %g1 - jmpl %g1, %g0 - flushw + sethi %hi(sparc32_execve), %g1 + or %g1, %lo(sparc32_execve), %g1 #endif +execve_merge: + flushw + jmpl %g1, %g0 + add %sp, PTREGS_OFF, %o0 + .align 32 sys_sparc_pipe: ba,pt %xcc, sys_sparc_pipe_real @@ -108,16 +112,11 @@ sys_clone: ret_from_syscall: /* Clear current_thread_info()->new_child. */ stb %g0, [%g6 + TI_NEW_CHILD] + ldx [%g6 + TI_FLAGS], %l0 call schedule_tail mov %g7, %o0 - ldx [%sp + PTREGS_OFF + PT_V9_I0], %o0 - brnz,pt %o0, ret_sys_call - ldx [%g6 + TI_FLAGS], %l0 - ldx [%sp + PTREGS_OFF + PT_V9_G1], %l1 - call %l1 - ldx [%sp + PTREGS_OFF + PT_V9_G2], %o0 ba,pt %xcc, ret_sys_call - mov 0, %o0 + ldx [%sp + PTREGS_OFF + PT_V9_I0], %o0 .globl sparc_exit .type sparc_exit,#function @@ -223,6 +222,7 @@ ret_sys_call: ldx [%sp + PTREGS_OFF + PT_V9_TNPC], %l1 ! pc = npc 2: + stb %g0, [%g6 + TI_SYS_NOERROR] /* System call success, clear Carry condition code. */ andn %g3, %g2, %g3 3: diff --git a/trunk/arch/sparc/kernel/systbls_64.S b/trunk/arch/sparc/kernel/systbls_64.S index acad343667d0..3a58e0d66f51 100644 --- a/trunk/arch/sparc/kernel/systbls_64.S +++ b/trunk/arch/sparc/kernel/systbls_64.S @@ -106,7 +106,7 @@ sys_call_table: /*40*/ .word sys_newlstat, sys_dup, sys_sparc_pipe, sys_times, sys_nis_syscall .word sys_umount, sys_setgid, sys_getgid, sys_signal, sys_geteuid /*50*/ .word sys_getegid, sys_acct, sys_memory_ordering, sys_nis_syscall, sys_ioctl - .word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys64_execve + .word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys_execve /*60*/ .word sys_umask, sys_chroot, sys_newfstat, sys_fstat64, sys_getpagesize .word sys_msync, sys_vfork, sys_pread64, sys_pwrite64, sys_nis_syscall /*70*/ .word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys_64_munmap, sys_mprotect diff --git a/trunk/arch/sparc/kernel/traps_64.c b/trunk/arch/sparc/kernel/traps_64.c index e7ecf1507d90..b66a77968f35 100644 --- a/trunk/arch/sparc/kernel/traps_64.c +++ b/trunk/arch/sparc/kernel/traps_64.c @@ -2688,8 +2688,8 @@ void __init trap_init(void) TI_PRE_COUNT != offsetof(struct thread_info, preempt_count) || TI_NEW_CHILD != offsetof(struct thread_info, new_child) || - TI_CURRENT_DS != offsetof(struct thread_info, - current_ds) || + TI_SYS_NOERROR != offsetof(struct thread_info, + syscall_noerror) || TI_RESTART_BLOCK != offsetof(struct thread_info, restart_block) || TI_KUNA_REGS != offsetof(struct thread_info, diff --git a/trunk/arch/sparc/mm/init_64.c b/trunk/arch/sparc/mm/init_64.c index 85be1ca539b2..9e28a118e6a4 100644 --- a/trunk/arch/sparc/mm/init_64.c +++ b/trunk/arch/sparc/mm/init_64.c @@ -624,7 +624,7 @@ static void __init inherit_prom_mappings(void) void prom_world(int enter) { if (!enter) - set_fs(get_fs()); + set_fs((mm_segment_t) { get_thread_current_ds() }); __asm__ __volatile__("flushw"); }