Skip to content

Commit

Permalink
[PATCH] ppc64: User tasks must have a valid thread.regs
Browse files Browse the repository at this point in the history
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 <anton@samba.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
  • Loading branch information
antonblanchard authored and Linus Torvalds committed Sep 22, 2004
1 parent 25b3b63 commit 3eac189
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 3 deletions.
13 changes: 12 additions & 1 deletion arch/ppc64/kernel/process.c
Expand Up @@ -397,11 +397,22 @@ void start_thread(struct pt_regs *regs, unsigned long fdptr, unsigned long sp)
/* 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;
Expand Down
20 changes: 18 additions & 2 deletions arch/ppc64/kernel/sys_ppc32.c
Expand Up @@ -633,8 +633,24 @@ long sys32_execve(unsigned long a0, unsigned long a1, unsigned long a2,
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(&regs->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;
Expand Down

0 comments on commit 3eac189

Please sign in to comment.