From: Anton Blanchard There have been reports of problems running UP ppc64 kernels where the kernel would die in the floating point save/restore code. It turns out kernel threads that call exec (and so become user tasks) do not have a valid thread.regs. This means init (pid 1) does not, it also means anything called out of exec_usermodehelper does not. Once that task has forked (eg init), then the thread.regs in the new task is correctly set. On UP do lazy save/restore of floating point regs. The SLES9 init is doing floating point (the debian version of init appears not to). The lack of thread.regs in init combined with the fact that it does floating point leads to our lazy FP save/restore code blowing up. There were other places where this problem exhibited itself in weird and interesting ways. If a task being exec'ed out of a kernel thread used more than 1MB of stack, it would be terminated due to the checks in arch/ppc64/mm/fault.c (looking for a valid thread.regs when extending the stack). We had a test case using the tux webserver that was failing due to this. Since we zero all registers in ELF_PLAT_INIT, I removed the extra memset in start_thread32. Signed-off-by: Anton Blanchard Signed-off-by: Andrew Morton --- 25-akpm/arch/ppc64/kernel/process.c | 13 ++++++++++++- 25-akpm/arch/ppc64/kernel/sys_ppc32.c | 20 ++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff -puN arch/ppc64/kernel/process.c~ppc64-user-tasks-must-have-a-valid-threadregs arch/ppc64/kernel/process.c --- 25/arch/ppc64/kernel/process.c~ppc64-user-tasks-must-have-a-valid-threadregs 2004-09-21 22:11:52.073158344 -0700 +++ 25-akpm/arch/ppc64/kernel/process.c 2004-09-21 22:11:52.079157432 -0700 @@ -397,11 +397,22 @@ void start_thread(struct pt_regs *regs, /* Check whether the e_entry function descriptor entries * need to be relocated before we can use them. */ - if ( load_addr != 0 ) { + if (load_addr != 0) { entry += load_addr; toc += load_addr; } + /* + * If we exec out of a kernel thread then thread.regs will not be + * set. Do it now. + */ + if (!current->thread.regs) { + unsigned long childregs = (unsigned long)current->thread_info + + THREAD_SIZE; + childregs -= sizeof(struct pt_regs); + current->thread.regs = childregs; + } + regs->nip = entry; regs->gpr[1] = sp; regs->gpr[2] = toc; diff -puN arch/ppc64/kernel/sys_ppc32.c~ppc64-user-tasks-must-have-a-valid-threadregs arch/ppc64/kernel/sys_ppc32.c --- 25/arch/ppc64/kernel/sys_ppc32.c~ppc64-user-tasks-must-have-a-valid-threadregs 2004-09-21 22:11:52.075158040 -0700 +++ 25-akpm/arch/ppc64/kernel/sys_ppc32.c 2004-09-21 22:11:52.080157280 -0700 @@ -633,8 +633,24 @@ out: void start_thread32(struct pt_regs* regs, unsigned long nip, unsigned long sp) { set_fs(USER_DS); - memset(regs->gpr, 0, sizeof(regs->gpr)); - memset(®s->ctr, 0, 4 * sizeof(regs->ctr)); + + /* + * If we exec out of a kernel thread then thread.regs will not be + * set. Do it now. + */ + if (!current->thread.regs) { + unsigned long childregs = (unsigned long)current->thread_info + + THREAD_SIZE; + childregs -= sizeof(struct pt_regs); + current->thread.regs = childregs; + } + + /* + * ELF_PLAT_INIT already clears all registers but it also sets r2. + * So just clear r2 here. + */ + regs->gpr[2] = 0; + regs->nip = nip; regs->gpr[1] = sp; regs->msr = MSR_USER32; _