Commit a051bc5b authored by David S. Miller's avatar David S. Miller

sparc64: Fix kernel thread stack termination.

Because of the silly way I set up the initial stack for
new kernel threads, there is a loop at the top of the
stack.

To fix this, properly add another stack frame that is copied
from the parent and terminate it in the child by setting
the frame pointer in that frame to zero.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3651751f
...@@ -657,20 +657,39 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, ...@@ -657,20 +657,39 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
struct task_struct *p, struct pt_regs *regs) struct task_struct *p, struct pt_regs *regs)
{ {
struct thread_info *t = task_thread_info(p); struct thread_info *t = task_thread_info(p);
struct sparc_stackf *parent_sf;
unsigned long child_stack_sz;
char *child_trap_frame; char *child_trap_frame;
int kernel_thread;
/* Calculate offset to stack_frame & pt_regs */ kernel_thread = (regs->tstate & TSTATE_PRIV) ? 1 : 0;
child_trap_frame = task_stack_page(p) + (THREAD_SIZE - (TRACEREG_SZ+STACKFRAME_SZ)); parent_sf = ((struct sparc_stackf *) regs) - 1;
memcpy(child_trap_frame, (((struct sparc_stackf *)regs)-1), (TRACEREG_SZ+STACKFRAME_SZ));
t->flags = (t->flags & ~((0xffUL << TI_FLAG_CWP_SHIFT) | (0xffUL << TI_FLAG_CURRENT_DS_SHIFT))) | /* Calculate offset to stack_frame & pt_regs */
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); (((regs->tstate + 1) & TSTATE_CWP) << TI_FLAG_CWP_SHIFT);
t->new_child = 1; t->new_child = 1;
t->ksp = ((unsigned long) child_trap_frame) - STACK_BIAS; t->ksp = ((unsigned long) child_trap_frame) - STACK_BIAS;
t->kregs = (struct pt_regs *)(child_trap_frame+sizeof(struct sparc_stackf)); t->kregs = (struct pt_regs *) (child_trap_frame +
sizeof(struct sparc_stackf));
t->fpsaved[0] = 0; t->fpsaved[0] = 0;
if (regs->tstate & TSTATE_PRIV) { if (kernel_thread) {
struct sparc_stackf *child_sf = (struct sparc_stackf *)
(child_trap_frame + (STACKFRAME_SZ + TRACEREG_SZ));
/* Zero terminate the stack backtrace. */
child_sf->fp = NULL;
t->kregs->u_regs[UREG_FP] =
((unsigned long) child_sf) - STACK_BIAS;
/* Special case, if we are spawning a kernel thread from /* Special case, if we are spawning a kernel thread from
* a userspace task (via KMOD, NFS, or similar) we must * a userspace task (via KMOD, NFS, or similar) we must
* disable performance counters in the child because the * disable performance counters in the child because the
...@@ -681,12 +700,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, ...@@ -681,12 +700,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
t->pcr_reg = 0; t->pcr_reg = 0;
t->flags &= ~_TIF_PERFCTR; t->flags &= ~_TIF_PERFCTR;
} }
t->kregs->u_regs[UREG_FP] = t->ksp;
t->flags |= ((long)ASI_P << TI_FLAG_CURRENT_DS_SHIFT); t->flags |= ((long)ASI_P << TI_FLAG_CURRENT_DS_SHIFT);
flush_register_windows();
memcpy((void *)(t->ksp + STACK_BIAS),
(void *)(regs->u_regs[UREG_FP] + STACK_BIAS),
sizeof(struct sparc_stackf));
t->kregs->u_regs[UREG_G6] = (unsigned long) t; t->kregs->u_regs[UREG_G6] = (unsigned long) t;
t->kregs->u_regs[UREG_G4] = (unsigned long) t->task; t->kregs->u_regs[UREG_G4] = (unsigned long) t->task;
} else { } else {
......
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