Commit 65941fab authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] ppc32: Fixes to the signal context code

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

This patch (which has been in my tree for some time now) does 2 things to
the ppc32 signal code:

- The new sys_swapcontext() syscall that we added recently (and which is
  _not_ yet used by glibc, so it's ok to change it slightly at this point,
  glibc kernel version check will limit us to 2.6.2 or 2.6.3) gets a new
  context size argument, so we can deal with future context size changes.

- When ucontext is get/set/swapped using the above syscall, the TLS (r2)
  is preserved (it's still saved/restored on signal entry & return though).

The equivalent of this patch is already in the ppc64 signal32.c emulation,
and it has no effect until glibc is updated to use the new syscall, which
should happen soon now, so please apply.
parent 91498a9a
......@@ -241,12 +241,16 @@ save_user_regs(struct pt_regs *regs, struct mcontext *frame, int sigret)
* (except for MSR).
*/
static int
restore_user_regs(struct pt_regs *regs, struct mcontext __user *sr)
restore_user_regs(struct pt_regs *regs, struct mcontext __user *sr, int sig)
{
unsigned long save_r2;
#ifdef CONFIG_ALTIVEC
unsigned long msr;
#endif
/* backup/restore the TLS as we don't want it to be modified */
if (!sig)
save_r2 = regs->gpr[2];
/* copy up to but not including MSR */
if (__copy_from_user(regs, &sr->mc_gregs, PT_MSR * sizeof(elf_greg_t)))
return 1;
......@@ -254,6 +258,8 @@ restore_user_regs(struct pt_regs *regs, struct mcontext __user *sr)
if (__copy_from_user(&regs->orig_gpr3, &sr->mc_gregs[PT_ORIG_R3],
GP_REGS_SIZE - PT_ORIG_R3 * sizeof(elf_greg_t)))
return 1;
if (!sig)
regs->gpr[2] = save_r2;
/* force the process to reload the FP registers from
current->thread when it next does FP instructions */
......@@ -359,7 +365,7 @@ handle_rt_signal(unsigned long sig, struct k_sigaction *ka,
force_sig(SIGSEGV, current);
}
static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs)
static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs, int sig)
{
sigset_t set;
struct mcontext *mcp;
......@@ -368,7 +374,7 @@ static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs)
|| __get_user(mcp, &ucp->uc_regs))
return -EFAULT;
restore_sigmask(&set);
if (restore_user_regs(regs, mcp))
if (restore_user_regs(regs, mcp, sig))
return -EFAULT;
return 0;
......@@ -376,10 +382,16 @@ static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs)
int sys_swapcontext(struct ucontext __user *old_ctx,
struct ucontext __user *new_ctx,
int r5, int r6, int r7, int r8, struct pt_regs *regs)
int ctx_size, int r6, int r7, int r8, struct pt_regs *regs)
{
unsigned char tmp;
/* Context size is for future use. Right now, we only make sure
* we are passed something we understand
*/
if (ctx_size < sizeof(struct ucontext))
return -EINVAL;
if (old_ctx != NULL) {
if (verify_area(VERIFY_WRITE, old_ctx, sizeof(*old_ctx))
|| save_user_regs(regs, &old_ctx->uc_mcontext, 0)
......@@ -406,7 +418,7 @@ int sys_swapcontext(struct ucontext __user *old_ctx,
* or if another thread unmaps the region containing the context.
* We kill the task with a SIGSEGV in this situation.
*/
if (do_setcontext(new_ctx, regs))
if (do_setcontext(new_ctx, regs, 0))
do_exit(SIGSEGV);
sigreturn_exit(regs);
/* doesn't actually return back to here */
......@@ -425,7 +437,7 @@ int sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
(regs->gpr[1] + __SIGNAL_FRAMESIZE + 16);
if (verify_area(VERIFY_READ, rt_sf, sizeof(struct rt_sigframe)))
goto bad;
if (do_setcontext(&rt_sf->uc, regs))
if (do_setcontext(&rt_sf->uc, regs, 1))
goto bad;
/*
......@@ -484,7 +496,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
if (save_user_regs(regs, &frame->mctx, __NR_sigreturn))
goto badframe;
if (put_user(regs->gpr[1], (unsigned long *)newsp))
if (put_user(regs->gpr[1], (unsigned long __user *)newsp))
goto badframe;
regs->gpr[1] = newsp;
regs->gpr[3] = sig;
......@@ -529,7 +541,7 @@ int sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
sr = (struct mcontext *) sigctx.regs;
if (verify_area(VERIFY_READ, sr, sizeof(*sr))
|| restore_user_regs(regs, sr))
|| restore_user_regs(regs, sr, 1))
goto badframe;
sigreturn_exit(regs); /* doesn't return */
......
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