2012-03-14 11:22:25 +08:00
|
|
|
/*
|
|
|
|
* linux/arch/m68k/kernel/signal.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
|
|
*
|
|
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
|
|
* License. See the file COPYING in the main directory of this archive
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Linux/m68k support by Hamish Macdonald
|
|
|
|
*
|
|
|
|
* 68060 fixes by Jesper Skov
|
|
|
|
*
|
|
|
|
* 1997-12-01 Modified for POSIX.1b signals by Andreas Schwab
|
|
|
|
*
|
|
|
|
* mathemu support by Roman Zippel
|
|
|
|
* (Note: fpstate in the signal context is completely ignored for the emulator
|
|
|
|
* and the internal floating point format is put on stack)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ++roman (07/09/96): implemented signal stacks (specially for tosemu on
|
|
|
|
* Atari :-) Current limitation: Only one sigstack can be active at one time.
|
|
|
|
* If a second signal with SA_ONSTACK set arrives while working on a sigstack,
|
|
|
|
* SA_ONSTACK is ignored. This behaviour avoids lots of trouble with nested
|
|
|
|
* signal handlers!
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/signal.h>
|
|
|
|
#include <linux/syscalls.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/wait.h>
|
|
|
|
#include <linux/ptrace.h>
|
|
|
|
#include <linux/unistd.h>
|
|
|
|
#include <linux/stddef.h>
|
|
|
|
#include <linux/highuid.h>
|
|
|
|
#include <linux/personality.h>
|
|
|
|
#include <linux/tty.h>
|
|
|
|
#include <linux/binfmts.h>
|
2016-09-20 05:01:38 +08:00
|
|
|
#include <linux/extable.h>
|
2022-02-10 02:20:45 +08:00
|
|
|
#include <linux/resume_user_mode.h>
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
#include <asm/setup.h>
|
2016-12-25 03:46:01 +08:00
|
|
|
#include <linux/uaccess.h>
|
2012-03-14 11:22:25 +08:00
|
|
|
#include <asm/traps.h>
|
|
|
|
#include <asm/ucontext.h>
|
2013-06-05 19:42:39 +08:00
|
|
|
#include <asm/cacheflush.h>
|
2012-03-14 11:22:25 +08:00
|
|
|
|
2011-03-22 11:39:27 +08:00
|
|
|
#ifdef CONFIG_MMU
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle the slight differences in classic 68k and ColdFire trap frames.
|
|
|
|
*/
|
|
|
|
#ifdef CONFIG_COLDFIRE
|
|
|
|
#define FORMAT 4
|
|
|
|
#define FMT4SIZE 0
|
2009-09-23 05:22:43 +08:00
|
|
|
#else
|
2012-03-14 11:22:25 +08:00
|
|
|
#define FORMAT 0
|
2020-05-27 21:39:42 +08:00
|
|
|
#define FMT4SIZE sizeof_field(struct frame, un.fmt4)
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
static const int frame_size_change[16] = {
|
2020-05-27 21:39:42 +08:00
|
|
|
[1] = -1, /* sizeof_field(struct frame, un.fmt1), */
|
|
|
|
[2] = sizeof_field(struct frame, un.fmt2),
|
|
|
|
[3] = sizeof_field(struct frame, un.fmt3),
|
2012-03-14 11:22:25 +08:00
|
|
|
[4] = FMT4SIZE,
|
2020-05-27 21:39:42 +08:00
|
|
|
[5] = -1, /* sizeof_field(struct frame, un.fmt5), */
|
|
|
|
[6] = -1, /* sizeof_field(struct frame, un.fmt6), */
|
|
|
|
[7] = sizeof_field(struct frame, un.fmt7),
|
|
|
|
[8] = -1, /* sizeof_field(struct frame, un.fmt8), */
|
|
|
|
[9] = sizeof_field(struct frame, un.fmt9),
|
|
|
|
[10] = sizeof_field(struct frame, un.fmta),
|
|
|
|
[11] = sizeof_field(struct frame, un.fmtb),
|
|
|
|
[12] = -1, /* sizeof_field(struct frame, un.fmtc), */
|
|
|
|
[13] = -1, /* sizeof_field(struct frame, un.fmtd), */
|
|
|
|
[14] = -1, /* sizeof_field(struct frame, un.fmte), */
|
|
|
|
[15] = -1, /* sizeof_field(struct frame, un.fmtf), */
|
2012-03-14 11:22:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static inline int frame_extra_sizes(int f)
|
|
|
|
{
|
|
|
|
return frame_size_change[f];
|
|
|
|
}
|
|
|
|
|
2016-12-26 03:33:17 +08:00
|
|
|
int fixup_exception(struct pt_regs *regs)
|
2012-03-14 11:22:25 +08:00
|
|
|
{
|
|
|
|
const struct exception_table_entry *fixup;
|
|
|
|
struct pt_regs *tregs;
|
|
|
|
|
|
|
|
/* Are we prepared to handle this kernel fault? */
|
|
|
|
fixup = search_exception_tables(regs->pc);
|
|
|
|
if (!fixup)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Create a new four word stack frame, discarding the old one. */
|
|
|
|
regs->stkadj = frame_extra_sizes(regs->format);
|
|
|
|
tregs = (struct pt_regs *)((long)regs + regs->stkadj);
|
|
|
|
tregs->vector = regs->vector;
|
|
|
|
tregs->format = FORMAT;
|
|
|
|
tregs->pc = fixup->fixup;
|
|
|
|
tregs->sr = regs->sr;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void push_cache (unsigned long vaddr)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Using the old cache_push_v() was really a big waste.
|
|
|
|
*
|
|
|
|
* What we are trying to do is to flush 8 bytes to ram.
|
|
|
|
* Flushing 2 cache lines of 16 bytes is much cheaper than
|
|
|
|
* flushing 1 or 2 pages, as previously done in
|
|
|
|
* cache_push_v().
|
|
|
|
* Jes
|
|
|
|
*/
|
|
|
|
if (CPU_IS_040) {
|
|
|
|
unsigned long temp;
|
|
|
|
|
|
|
|
__asm__ __volatile__ (".chip 68040\n\t"
|
|
|
|
"nop\n\t"
|
|
|
|
"ptestr (%1)\n\t"
|
|
|
|
"movec %%mmusr,%0\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: "=r" (temp)
|
|
|
|
: "a" (vaddr));
|
|
|
|
|
|
|
|
temp &= PAGE_MASK;
|
|
|
|
temp |= vaddr & ~PAGE_MASK;
|
|
|
|
|
|
|
|
__asm__ __volatile__ (".chip 68040\n\t"
|
|
|
|
"nop\n\t"
|
|
|
|
"cpushl %%bc,(%0)\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: : "a" (temp));
|
|
|
|
}
|
|
|
|
else if (CPU_IS_060) {
|
|
|
|
unsigned long temp;
|
|
|
|
__asm__ __volatile__ (".chip 68060\n\t"
|
|
|
|
"plpar (%0)\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: "=a" (temp)
|
|
|
|
: "0" (vaddr));
|
|
|
|
__asm__ __volatile__ (".chip 68060\n\t"
|
|
|
|
"cpushl %%bc,(%0)\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: : "a" (temp));
|
|
|
|
} else if (!CPU_IS_COLDFIRE) {
|
|
|
|
/*
|
|
|
|
* 68030/68020 have no writeback cache;
|
|
|
|
* still need to clear icache.
|
|
|
|
* Note that vaddr is guaranteed to be long word aligned.
|
|
|
|
*/
|
|
|
|
unsigned long temp;
|
|
|
|
asm volatile ("movec %%cacr,%0" : "=r" (temp));
|
|
|
|
temp += 4;
|
|
|
|
asm volatile ("movec %0,%%caar\n\t"
|
|
|
|
"movec %1,%%cacr"
|
|
|
|
: : "r" (vaddr), "r" (temp));
|
|
|
|
asm volatile ("movec %0,%%caar\n\t"
|
|
|
|
"movec %1,%%cacr"
|
|
|
|
: : "r" (vaddr + 4), "r" (temp));
|
2013-06-05 19:42:39 +08:00
|
|
|
} else {
|
|
|
|
/* CPU_IS_COLDFIRE */
|
|
|
|
#if defined(CONFIG_CACHE_COPYBACK)
|
|
|
|
flush_cf_dcache(0, DCACHE_MAX_ADDR);
|
|
|
|
#endif
|
|
|
|
/* Invalidate instruction cache for the pushed bytes */
|
|
|
|
clear_cf_icache(vaddr, vaddr + 8);
|
2012-03-14 11:22:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void adjustformat(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void save_a5_state(struct sigcontext *sc, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* CONFIG_MMU */
|
|
|
|
|
|
|
|
void ret_from_user_signal(void);
|
|
|
|
void ret_from_user_rt_signal(void);
|
|
|
|
|
|
|
|
static inline int frame_extra_sizes(int f)
|
|
|
|
{
|
|
|
|
/* No frame size adjustments required on non-MMU CPUs */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void adjustformat(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* set format byte to make stack appear modulo 4, which it will
|
|
|
|
* be when doing the rte
|
|
|
|
*/
|
|
|
|
regs->format = 0x4;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void save_a5_state(struct sigcontext *sc, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
sc->sc_a5 = ((struct switch_stack *)regs - 1)->a5;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void push_cache(unsigned long vaddr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_MMU */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do a signal return; undo the signal stack.
|
|
|
|
*
|
|
|
|
* Keep the return code on the stack quadword aligned!
|
|
|
|
* That makes the cache flush below easier.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct sigframe
|
|
|
|
{
|
|
|
|
char __user *pretcode;
|
|
|
|
int sig;
|
|
|
|
int code;
|
|
|
|
struct sigcontext __user *psc;
|
|
|
|
char retcode[8];
|
|
|
|
unsigned long extramask[_NSIG_WORDS-1];
|
|
|
|
struct sigcontext sc;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct rt_sigframe
|
|
|
|
{
|
|
|
|
char __user *pretcode;
|
|
|
|
int sig;
|
|
|
|
struct siginfo __user *pinfo;
|
|
|
|
void __user *puc;
|
|
|
|
char retcode[8];
|
|
|
|
struct siginfo info;
|
|
|
|
struct ucontext uc;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define FPCONTEXT_SIZE 216
|
|
|
|
#define uc_fpstate uc_filler[0]
|
|
|
|
#define uc_formatvec uc_filler[FPCONTEXT_SIZE/4]
|
|
|
|
#define uc_extra uc_filler[FPCONTEXT_SIZE/4+1]
|
|
|
|
|
|
|
|
#ifdef CONFIG_FPU
|
|
|
|
|
|
|
|
static unsigned char fpu_version; /* version number of fpu, set by setup_frame */
|
|
|
|
|
|
|
|
static inline int restore_fpu_state(struct sigcontext *sc)
|
|
|
|
{
|
|
|
|
int err = 1;
|
|
|
|
|
|
|
|
if (FPU_IS_EMU) {
|
|
|
|
/* restore registers */
|
|
|
|
memcpy(current->thread.fpcntl, sc->sc_fpcntl, 12);
|
|
|
|
memcpy(current->thread.fp, sc->sc_fpregs, 24);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) {
|
|
|
|
/* Verify the frame format. */
|
|
|
|
if (!(CPU_IS_060 || CPU_IS_COLDFIRE) &&
|
|
|
|
(sc->sc_fpstate[0] != fpu_version))
|
|
|
|
goto out;
|
|
|
|
if (CPU_IS_020_OR_030) {
|
|
|
|
if (m68k_fputype & FPU_68881 &&
|
|
|
|
!(sc->sc_fpstate[1] == 0x18 || sc->sc_fpstate[1] == 0xb4))
|
|
|
|
goto out;
|
|
|
|
if (m68k_fputype & FPU_68882 &&
|
|
|
|
!(sc->sc_fpstate[1] == 0x38 || sc->sc_fpstate[1] == 0xd4))
|
|
|
|
goto out;
|
|
|
|
} else if (CPU_IS_040) {
|
|
|
|
if (!(sc->sc_fpstate[1] == 0x00 ||
|
|
|
|
sc->sc_fpstate[1] == 0x28 ||
|
|
|
|
sc->sc_fpstate[1] == 0x60))
|
|
|
|
goto out;
|
|
|
|
} else if (CPU_IS_060) {
|
|
|
|
if (!(sc->sc_fpstate[3] == 0x00 ||
|
|
|
|
sc->sc_fpstate[3] == 0x60 ||
|
|
|
|
sc->sc_fpstate[3] == 0xe0))
|
|
|
|
goto out;
|
|
|
|
} else if (CPU_IS_COLDFIRE) {
|
|
|
|
if (!(sc->sc_fpstate[0] == 0x00 ||
|
|
|
|
sc->sc_fpstate[0] == 0x05 ||
|
|
|
|
sc->sc_fpstate[0] == 0xe5))
|
|
|
|
goto out;
|
|
|
|
} else
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (CPU_IS_COLDFIRE) {
|
|
|
|
__asm__ volatile ("fmovemd %0,%%fp0-%%fp1\n\t"
|
|
|
|
"fmovel %1,%%fpcr\n\t"
|
|
|
|
"fmovel %2,%%fpsr\n\t"
|
|
|
|
"fmovel %3,%%fpiar"
|
|
|
|
: /* no outputs */
|
|
|
|
: "m" (sc->sc_fpregs[0]),
|
|
|
|
"m" (sc->sc_fpcntl[0]),
|
|
|
|
"m" (sc->sc_fpcntl[1]),
|
|
|
|
"m" (sc->sc_fpcntl[2]));
|
|
|
|
} else {
|
|
|
|
__asm__ volatile (".chip 68k/68881\n\t"
|
|
|
|
"fmovemx %0,%%fp0-%%fp1\n\t"
|
|
|
|
"fmoveml %1,%%fpcr/%%fpsr/%%fpiar\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: /* no outputs */
|
|
|
|
: "m" (*sc->sc_fpregs),
|
|
|
|
"m" (*sc->sc_fpcntl));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CPU_IS_COLDFIRE) {
|
|
|
|
__asm__ volatile ("frestore %0" : : "m" (*sc->sc_fpstate));
|
|
|
|
} else {
|
|
|
|
__asm__ volatile (".chip 68k/68881\n\t"
|
|
|
|
"frestore %0\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: : "m" (*sc->sc_fpstate));
|
|
|
|
}
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int rt_restore_fpu_state(struct ucontext __user *uc)
|
|
|
|
{
|
|
|
|
unsigned char fpstate[FPCONTEXT_SIZE];
|
|
|
|
int context_size = CPU_IS_060 ? 8 : (CPU_IS_COLDFIRE ? 12 : 0);
|
|
|
|
fpregset_t fpregs;
|
|
|
|
int err = 1;
|
|
|
|
|
|
|
|
if (FPU_IS_EMU) {
|
|
|
|
/* restore fpu control register */
|
|
|
|
if (__copy_from_user(current->thread.fpcntl,
|
|
|
|
uc->uc_mcontext.fpregs.f_fpcntl, 12))
|
|
|
|
goto out;
|
|
|
|
/* restore all other fpu register */
|
|
|
|
if (__copy_from_user(current->thread.fp,
|
|
|
|
uc->uc_mcontext.fpregs.f_fpregs, 96))
|
|
|
|
goto out;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (__get_user(*(long *)fpstate, (long __user *)&uc->uc_fpstate))
|
|
|
|
goto out;
|
|
|
|
if (CPU_IS_060 ? fpstate[2] : fpstate[0]) {
|
|
|
|
if (!(CPU_IS_060 || CPU_IS_COLDFIRE))
|
|
|
|
context_size = fpstate[1];
|
|
|
|
/* Verify the frame format. */
|
|
|
|
if (!(CPU_IS_060 || CPU_IS_COLDFIRE) &&
|
|
|
|
(fpstate[0] != fpu_version))
|
|
|
|
goto out;
|
|
|
|
if (CPU_IS_020_OR_030) {
|
|
|
|
if (m68k_fputype & FPU_68881 &&
|
|
|
|
!(context_size == 0x18 || context_size == 0xb4))
|
|
|
|
goto out;
|
|
|
|
if (m68k_fputype & FPU_68882 &&
|
|
|
|
!(context_size == 0x38 || context_size == 0xd4))
|
|
|
|
goto out;
|
|
|
|
} else if (CPU_IS_040) {
|
|
|
|
if (!(context_size == 0x00 ||
|
|
|
|
context_size == 0x28 ||
|
|
|
|
context_size == 0x60))
|
|
|
|
goto out;
|
|
|
|
} else if (CPU_IS_060) {
|
|
|
|
if (!(fpstate[3] == 0x00 ||
|
|
|
|
fpstate[3] == 0x60 ||
|
|
|
|
fpstate[3] == 0xe0))
|
|
|
|
goto out;
|
|
|
|
} else if (CPU_IS_COLDFIRE) {
|
|
|
|
if (!(fpstate[3] == 0x00 ||
|
|
|
|
fpstate[3] == 0x05 ||
|
|
|
|
fpstate[3] == 0xe5))
|
|
|
|
goto out;
|
|
|
|
} else
|
|
|
|
goto out;
|
|
|
|
if (__copy_from_user(&fpregs, &uc->uc_mcontext.fpregs,
|
|
|
|
sizeof(fpregs)))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (CPU_IS_COLDFIRE) {
|
|
|
|
__asm__ volatile ("fmovemd %0,%%fp0-%%fp7\n\t"
|
|
|
|
"fmovel %1,%%fpcr\n\t"
|
|
|
|
"fmovel %2,%%fpsr\n\t"
|
|
|
|
"fmovel %3,%%fpiar"
|
|
|
|
: /* no outputs */
|
|
|
|
: "m" (fpregs.f_fpregs[0]),
|
|
|
|
"m" (fpregs.f_fpcntl[0]),
|
|
|
|
"m" (fpregs.f_fpcntl[1]),
|
|
|
|
"m" (fpregs.f_fpcntl[2]));
|
|
|
|
} else {
|
|
|
|
__asm__ volatile (".chip 68k/68881\n\t"
|
|
|
|
"fmovemx %0,%%fp0-%%fp7\n\t"
|
|
|
|
"fmoveml %1,%%fpcr/%%fpsr/%%fpiar\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: /* no outputs */
|
|
|
|
: "m" (*fpregs.f_fpregs),
|
|
|
|
"m" (*fpregs.f_fpcntl));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (context_size &&
|
|
|
|
__copy_from_user(fpstate + 4, (long __user *)&uc->uc_fpstate + 1,
|
|
|
|
context_size))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (CPU_IS_COLDFIRE) {
|
|
|
|
__asm__ volatile ("frestore %0" : : "m" (*fpstate));
|
|
|
|
} else {
|
|
|
|
__asm__ volatile (".chip 68k/68881\n\t"
|
|
|
|
"frestore %0\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: : "m" (*fpstate));
|
|
|
|
}
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up a signal frame.
|
|
|
|
*/
|
|
|
|
static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
if (FPU_IS_EMU) {
|
|
|
|
/* save registers */
|
|
|
|
memcpy(sc->sc_fpcntl, current->thread.fpcntl, 12);
|
|
|
|
memcpy(sc->sc_fpregs, current->thread.fp, 24);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CPU_IS_COLDFIRE) {
|
|
|
|
__asm__ volatile ("fsave %0"
|
|
|
|
: : "m" (*sc->sc_fpstate) : "memory");
|
|
|
|
} else {
|
|
|
|
__asm__ volatile (".chip 68k/68881\n\t"
|
|
|
|
"fsave %0\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: : "m" (*sc->sc_fpstate) : "memory");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) {
|
|
|
|
fpu_version = sc->sc_fpstate[0];
|
2021-07-26 01:19:00 +08:00
|
|
|
if (CPU_IS_020_OR_030 && !regs->stkadj &&
|
2012-03-14 11:22:25 +08:00
|
|
|
regs->vector >= (VEC_FPBRUC * 4) &&
|
|
|
|
regs->vector <= (VEC_FPNAN * 4)) {
|
|
|
|
/* Clear pending exception in 68882 idle frame */
|
|
|
|
if (*(unsigned short *) sc->sc_fpstate == 0x1f38)
|
|
|
|
sc->sc_fpstate[0x38] |= 1 << 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CPU_IS_COLDFIRE) {
|
|
|
|
__asm__ volatile ("fmovemd %%fp0-%%fp1,%0\n\t"
|
|
|
|
"fmovel %%fpcr,%1\n\t"
|
|
|
|
"fmovel %%fpsr,%2\n\t"
|
|
|
|
"fmovel %%fpiar,%3"
|
|
|
|
: "=m" (sc->sc_fpregs[0]),
|
|
|
|
"=m" (sc->sc_fpcntl[0]),
|
|
|
|
"=m" (sc->sc_fpcntl[1]),
|
|
|
|
"=m" (sc->sc_fpcntl[2])
|
|
|
|
: /* no inputs */
|
|
|
|
: "memory");
|
|
|
|
} else {
|
|
|
|
__asm__ volatile (".chip 68k/68881\n\t"
|
|
|
|
"fmovemx %%fp0-%%fp1,%0\n\t"
|
|
|
|
"fmoveml %%fpcr/%%fpsr/%%fpiar,%1\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: "=m" (*sc->sc_fpregs),
|
|
|
|
"=m" (*sc->sc_fpcntl)
|
|
|
|
: /* no inputs */
|
|
|
|
: "memory");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int rt_save_fpu_state(struct ucontext __user *uc, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
unsigned char fpstate[FPCONTEXT_SIZE];
|
|
|
|
int context_size = CPU_IS_060 ? 8 : (CPU_IS_COLDFIRE ? 12 : 0);
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (FPU_IS_EMU) {
|
|
|
|
/* save fpu control register */
|
|
|
|
err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpcntl,
|
|
|
|
current->thread.fpcntl, 12);
|
|
|
|
/* save all other fpu register */
|
|
|
|
err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpregs,
|
|
|
|
current->thread.fp, 96);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CPU_IS_COLDFIRE) {
|
|
|
|
__asm__ volatile ("fsave %0" : : "m" (*fpstate) : "memory");
|
|
|
|
} else {
|
|
|
|
__asm__ volatile (".chip 68k/68881\n\t"
|
|
|
|
"fsave %0\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: : "m" (*fpstate) : "memory");
|
|
|
|
}
|
|
|
|
|
|
|
|
err |= __put_user(*(long *)fpstate, (long __user *)&uc->uc_fpstate);
|
|
|
|
if (CPU_IS_060 ? fpstate[2] : fpstate[0]) {
|
|
|
|
fpregset_t fpregs;
|
|
|
|
if (!(CPU_IS_060 || CPU_IS_COLDFIRE))
|
|
|
|
context_size = fpstate[1];
|
|
|
|
fpu_version = fpstate[0];
|
2021-07-26 01:19:00 +08:00
|
|
|
if (CPU_IS_020_OR_030 && !regs->stkadj &&
|
2012-03-14 11:22:25 +08:00
|
|
|
regs->vector >= (VEC_FPBRUC * 4) &&
|
|
|
|
regs->vector <= (VEC_FPNAN * 4)) {
|
|
|
|
/* Clear pending exception in 68882 idle frame */
|
|
|
|
if (*(unsigned short *) fpstate == 0x1f38)
|
|
|
|
fpstate[0x38] |= 1 << 3;
|
|
|
|
}
|
|
|
|
if (CPU_IS_COLDFIRE) {
|
|
|
|
__asm__ volatile ("fmovemd %%fp0-%%fp7,%0\n\t"
|
|
|
|
"fmovel %%fpcr,%1\n\t"
|
|
|
|
"fmovel %%fpsr,%2\n\t"
|
|
|
|
"fmovel %%fpiar,%3"
|
|
|
|
: "=m" (fpregs.f_fpregs[0]),
|
|
|
|
"=m" (fpregs.f_fpcntl[0]),
|
|
|
|
"=m" (fpregs.f_fpcntl[1]),
|
|
|
|
"=m" (fpregs.f_fpcntl[2])
|
|
|
|
: /* no inputs */
|
|
|
|
: "memory");
|
|
|
|
} else {
|
|
|
|
__asm__ volatile (".chip 68k/68881\n\t"
|
|
|
|
"fmovemx %%fp0-%%fp7,%0\n\t"
|
|
|
|
"fmoveml %%fpcr/%%fpsr/%%fpiar,%1\n\t"
|
|
|
|
".chip 68k"
|
|
|
|
: "=m" (*fpregs.f_fpregs),
|
|
|
|
"=m" (*fpregs.f_fpcntl)
|
|
|
|
: /* no inputs */
|
|
|
|
: "memory");
|
|
|
|
}
|
|
|
|
err |= copy_to_user(&uc->uc_mcontext.fpregs, &fpregs,
|
|
|
|
sizeof(fpregs));
|
|
|
|
}
|
|
|
|
if (context_size)
|
|
|
|
err |= copy_to_user((long __user *)&uc->uc_fpstate + 1, fpstate + 4,
|
|
|
|
context_size);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* CONFIG_FPU */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For the case with no FPU configured these all do nothing.
|
|
|
|
*/
|
|
|
|
static inline int restore_fpu_state(struct sigcontext *sc)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int rt_restore_fpu_state(struct ucontext __user *uc)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int rt_save_fpu_state(struct ucontext __user *uc, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_FPU */
|
|
|
|
|
2018-04-03 04:10:57 +08:00
|
|
|
static inline void siginfo_build_tests(void)
|
|
|
|
{
|
2018-04-08 17:42:04 +08:00
|
|
|
/*
|
|
|
|
* This needs to be tested on m68k as it has a lesser
|
|
|
|
* alignment requirement than x86 and that can cause surprises.
|
2018-04-03 04:10:57 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* This is part of the ABI and can never change in size: */
|
|
|
|
BUILD_BUG_ON(sizeof(siginfo_t) != 128);
|
|
|
|
|
2018-04-08 17:42:04 +08:00
|
|
|
/* Ensure the known fields never change in location */
|
2018-04-03 04:10:57 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_signo) != 0);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_errno) != 4);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_code) != 8);
|
|
|
|
|
|
|
|
/* _kill */
|
2018-04-08 17:42:04 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_pid) != 0x0c);
|
2018-04-03 04:10:57 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_uid) != 0x10);
|
|
|
|
|
|
|
|
/* _timer */
|
2018-04-08 17:42:04 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_tid) != 0x0c);
|
2018-04-03 04:10:57 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_overrun) != 0x10);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_value) != 0x14);
|
|
|
|
|
|
|
|
/* _rt */
|
2018-04-08 17:42:04 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_pid) != 0x0c);
|
2018-04-03 04:10:57 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_uid) != 0x10);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_value) != 0x14);
|
|
|
|
|
|
|
|
/* _sigchld */
|
2018-04-08 17:42:04 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_pid) != 0x0c);
|
2018-04-03 04:10:57 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_uid) != 0x10);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_status) != 0x14);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_utime) != 0x18);
|
2018-04-08 17:42:04 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_stime) != 0x1c);
|
2018-04-03 04:10:57 +08:00
|
|
|
|
|
|
|
/* _sigfault */
|
2018-04-08 17:42:04 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_addr) != 0x0c);
|
2018-04-03 04:10:57 +08:00
|
|
|
|
|
|
|
/* _sigfault._mcerr */
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_addr_lsb) != 0x10);
|
|
|
|
|
|
|
|
/* _sigfault._addr_bnd */
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_lower) != 0x12);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_upper) != 0x16);
|
|
|
|
|
|
|
|
/* _sigfault._addr_pkey */
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_pkey) != 0x12);
|
|
|
|
|
2021-04-08 18:36:00 +08:00
|
|
|
/* _sigfault._perf */
|
2021-05-03 06:28:31 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_perf_data) != 0x10);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_perf_type) != 0x14);
|
signal: Deliver SIGTRAP on perf event asynchronously if blocked
With SIGTRAP on perf events, we have encountered termination of
processes due to user space attempting to block delivery of SIGTRAP.
Consider this case:
<set up SIGTRAP on a perf event>
...
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGTRAP | <and others>);
sigprocmask(SIG_BLOCK, &s, ...);
...
<perf event triggers>
When the perf event triggers, while SIGTRAP is blocked, force_sig_perf()
will force the signal, but revert back to the default handler, thus
terminating the task.
This makes sense for error conditions, but not so much for explicitly
requested monitoring. However, the expectation is still that signals
generated by perf events are synchronous, which will no longer be the
case if the signal is blocked and delivered later.
To give user space the ability to clearly distinguish synchronous from
asynchronous signals, introduce siginfo_t::si_perf_flags and
TRAP_PERF_FLAG_ASYNC (opted for flags in case more binary information is
required in future).
The resolution to the problem is then to (a) no longer force the signal
(avoiding the terminations), but (b) tell user space via si_perf_flags
if the signal was synchronous or not, so that such signals can be
handled differently (e.g. let user space decide to ignore or consider
the data imprecise).
The alternative of making the kernel ignore SIGTRAP on perf events if
the signal is blocked may work for some usecases, but likely causes
issues in others that then have to revert back to interception of
sigprocmask() (which we want to avoid). [ A concrete example: when using
breakpoint perf events to track data-flow, in a region of code where
signals are blocked, data-flow can no longer be tracked accurately.
When a relevant asynchronous signal is received after unblocking the
signal, the data-flow tracking logic needs to know its state is
imprecise. ]
Fixes: 97ba62b27867 ("perf: Add support for SIGTRAP on perf events")
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Marco Elver <elver@google.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Geert Uytterhoeven <geert@linux-m68k.org>
Tested-by: Dmitry Vyukov <dvyukov@google.com>
Link: https://lore.kernel.org/r/20220404111204.935357-1-elver@google.com
2022-04-04 19:12:04 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_perf_flags) != 0x18);
|
2021-04-08 18:36:00 +08:00
|
|
|
|
2018-04-03 04:10:57 +08:00
|
|
|
/* _sigpoll */
|
2018-04-08 17:42:04 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_band) != 0x0c);
|
2018-04-03 04:10:57 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_fd) != 0x10);
|
|
|
|
|
|
|
|
/* _sigsys */
|
2018-04-08 17:42:04 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_call_addr) != 0x0c);
|
2018-04-03 04:10:57 +08:00
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_syscall) != 0x10);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_arch) != 0x14);
|
|
|
|
|
|
|
|
/* any new si_fields should be added here */
|
|
|
|
}
|
|
|
|
|
2012-03-14 11:22:25 +08:00
|
|
|
static int mangle_kernel_stack(struct pt_regs *regs, int formatvec,
|
|
|
|
void __user *fp)
|
|
|
|
{
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
int extra = frame_extra_sizes(formatvec >> 12);
|
|
|
|
char buf[sizeof_field(struct frame, un)];
|
|
|
|
|
|
|
|
if (extra < 0) {
|
2012-03-14 11:22:25 +08:00
|
|
|
/*
|
|
|
|
* user process trying to return with weird frame format
|
|
|
|
*/
|
2016-12-07 02:57:37 +08:00
|
|
|
pr_debug("user process returning with weird frame format\n");
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
return -1;
|
2012-03-14 11:22:25 +08:00
|
|
|
}
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
if (extra && copy_from_user(buf, fp, extra))
|
|
|
|
return -1;
|
|
|
|
regs->format = formatvec >> 12;
|
|
|
|
regs->vector = formatvec & 0xfff;
|
|
|
|
if (extra) {
|
|
|
|
void *p = (struct switch_stack *)regs - 1;
|
|
|
|
struct frame *new = (void *)regs - extra;
|
|
|
|
int size = sizeof(struct pt_regs)+sizeof(struct switch_stack);
|
|
|
|
|
|
|
|
memmove(p - extra, p, size);
|
|
|
|
memcpy(p - extra + size, buf, extra);
|
|
|
|
current->thread.esp0 = (unsigned long)&new->ptregs;
|
|
|
|
#ifdef CONFIG_M68040
|
|
|
|
/* on 68040 complete pending writebacks if any */
|
|
|
|
if (new->ptregs.format == 7) // bus error frame
|
|
|
|
berr_040cleanup(new);
|
2012-03-14 11:22:25 +08:00
|
|
|
#endif
|
|
|
|
}
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
return extra;
|
2012-03-14 11:22:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __user *fp)
|
|
|
|
{
|
|
|
|
int formatvec;
|
|
|
|
struct sigcontext context;
|
|
|
|
|
2018-04-03 04:10:57 +08:00
|
|
|
siginfo_build_tests();
|
|
|
|
|
2012-03-14 11:22:25 +08:00
|
|
|
/* Always make any pending restarted system calls return -EINTR */
|
2015-02-13 07:01:14 +08:00
|
|
|
current->restart_block.fn = do_no_restart_syscall;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
/* get previous context */
|
|
|
|
if (copy_from_user(&context, usc, sizeof(context)))
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
return -1;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
/* restore passed registers */
|
|
|
|
regs->d0 = context.sc_d0;
|
|
|
|
regs->d1 = context.sc_d1;
|
|
|
|
regs->a0 = context.sc_a0;
|
|
|
|
regs->a1 = context.sc_a1;
|
|
|
|
regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff);
|
|
|
|
regs->pc = context.sc_pc;
|
|
|
|
regs->orig_d0 = -1; /* disable syscall checks */
|
|
|
|
wrusp(context.sc_usp);
|
|
|
|
formatvec = context.sc_formatvec;
|
|
|
|
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
if (restore_fpu_state(&context))
|
|
|
|
return -1;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
return mangle_kernel_stack(regs, formatvec, fp);
|
2012-03-14 11:22:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw,
|
|
|
|
struct ucontext __user *uc)
|
|
|
|
{
|
|
|
|
int temp;
|
|
|
|
greg_t __user *gregs = uc->uc_mcontext.gregs;
|
|
|
|
unsigned long usp;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* Always make any pending restarted system calls return -EINTR */
|
2015-02-13 07:01:14 +08:00
|
|
|
current->restart_block.fn = do_no_restart_syscall;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
err = __get_user(temp, &uc->uc_mcontext.version);
|
|
|
|
if (temp != MCONTEXT_VERSION)
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
return -1;
|
2012-03-14 11:22:25 +08:00
|
|
|
/* restore passed registers */
|
|
|
|
err |= __get_user(regs->d0, &gregs[0]);
|
|
|
|
err |= __get_user(regs->d1, &gregs[1]);
|
|
|
|
err |= __get_user(regs->d2, &gregs[2]);
|
|
|
|
err |= __get_user(regs->d3, &gregs[3]);
|
|
|
|
err |= __get_user(regs->d4, &gregs[4]);
|
|
|
|
err |= __get_user(regs->d5, &gregs[5]);
|
|
|
|
err |= __get_user(sw->d6, &gregs[6]);
|
|
|
|
err |= __get_user(sw->d7, &gregs[7]);
|
|
|
|
err |= __get_user(regs->a0, &gregs[8]);
|
|
|
|
err |= __get_user(regs->a1, &gregs[9]);
|
|
|
|
err |= __get_user(regs->a2, &gregs[10]);
|
|
|
|
err |= __get_user(sw->a3, &gregs[11]);
|
|
|
|
err |= __get_user(sw->a4, &gregs[12]);
|
|
|
|
err |= __get_user(sw->a5, &gregs[13]);
|
|
|
|
err |= __get_user(sw->a6, &gregs[14]);
|
|
|
|
err |= __get_user(usp, &gregs[15]);
|
|
|
|
wrusp(usp);
|
|
|
|
err |= __get_user(regs->pc, &gregs[16]);
|
|
|
|
err |= __get_user(temp, &gregs[17]);
|
|
|
|
regs->sr = (regs->sr & 0xff00) | (temp & 0xff);
|
|
|
|
regs->orig_d0 = -1; /* disable syscall checks */
|
|
|
|
err |= __get_user(temp, &uc->uc_formatvec);
|
|
|
|
|
|
|
|
err |= rt_restore_fpu_state(uc);
|
2012-12-23 16:06:03 +08:00
|
|
|
err |= restore_altstack(&uc->uc_stack);
|
2012-03-14 11:22:25 +08:00
|
|
|
|
2012-12-23 16:06:03 +08:00
|
|
|
if (err)
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
return -1;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
return mangle_kernel_stack(regs, temp, &uc->uc_extra);
|
2012-03-14 11:22:25 +08:00
|
|
|
}
|
|
|
|
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
asmlinkage void *do_sigreturn(struct pt_regs *regs, struct switch_stack *sw)
|
2012-03-14 11:22:25 +08:00
|
|
|
{
|
|
|
|
unsigned long usp = rdusp();
|
|
|
|
struct sigframe __user *frame = (struct sigframe __user *)(usp - 4);
|
|
|
|
sigset_t set;
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
int size;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
Remove 'type' argument from access_ok() function
Nobody has actually used the type (VERIFY_READ vs VERIFY_WRITE) argument
of the user address range verification function since we got rid of the
old racy i386-only code to walk page tables by hand.
It existed because the original 80386 would not honor the write protect
bit when in kernel mode, so you had to do COW by hand before doing any
user access. But we haven't supported that in a long time, and these
days the 'type' argument is a purely historical artifact.
A discussion about extending 'user_access_begin()' to do the range
checking resulted this patch, because there is no way we're going to
move the old VERIFY_xyz interface to that model. And it's best done at
the end of the merge window when I've done most of my merges, so let's
just get this done once and for all.
This patch was mostly done with a sed-script, with manual fix-ups for
the cases that weren't of the trivial 'access_ok(VERIFY_xyz' form.
There were a couple of notable cases:
- csky still had the old "verify_area()" name as an alias.
- the iter_iov code had magical hardcoded knowledge of the actual
values of VERIFY_{READ,WRITE} (not that they mattered, since nothing
really used it)
- microblaze used the type argument for a debug printout
but other than those oddities this should be a total no-op patch.
I tried to fix up all architectures, did fairly extensive grepping for
access_ok() uses, and the changes are trivial, but I may have missed
something. Any missed conversion should be trivially fixable, though.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2019-01-04 10:57:57 +08:00
|
|
|
if (!access_ok(frame, sizeof(*frame)))
|
2012-03-14 11:22:25 +08:00
|
|
|
goto badframe;
|
|
|
|
if (__get_user(set.sig[0], &frame->sc.sc_mask) ||
|
|
|
|
(_NSIG_WORDS > 1 &&
|
|
|
|
__copy_from_user(&set.sig[1], &frame->extramask,
|
|
|
|
sizeof(frame->extramask))))
|
|
|
|
goto badframe;
|
|
|
|
|
2012-05-11 08:57:59 +08:00
|
|
|
set_current_blocked(&set);
|
2012-03-14 11:22:25 +08:00
|
|
|
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
size = restore_sigcontext(regs, &frame->sc, frame + 1);
|
|
|
|
if (size < 0)
|
2012-03-14 11:22:25 +08:00
|
|
|
goto badframe;
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
return (void *)sw - size;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
badframe:
|
2019-05-23 23:17:27 +08:00
|
|
|
force_sig(SIGSEGV);
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
return sw;
|
2012-03-14 11:22:25 +08:00
|
|
|
}
|
|
|
|
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
asmlinkage void *do_rt_sigreturn(struct pt_regs *regs, struct switch_stack *sw)
|
2012-03-14 11:22:25 +08:00
|
|
|
{
|
|
|
|
unsigned long usp = rdusp();
|
|
|
|
struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(usp - 4);
|
|
|
|
sigset_t set;
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
int size;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
Remove 'type' argument from access_ok() function
Nobody has actually used the type (VERIFY_READ vs VERIFY_WRITE) argument
of the user address range verification function since we got rid of the
old racy i386-only code to walk page tables by hand.
It existed because the original 80386 would not honor the write protect
bit when in kernel mode, so you had to do COW by hand before doing any
user access. But we haven't supported that in a long time, and these
days the 'type' argument is a purely historical artifact.
A discussion about extending 'user_access_begin()' to do the range
checking resulted this patch, because there is no way we're going to
move the old VERIFY_xyz interface to that model. And it's best done at
the end of the merge window when I've done most of my merges, so let's
just get this done once and for all.
This patch was mostly done with a sed-script, with manual fix-ups for
the cases that weren't of the trivial 'access_ok(VERIFY_xyz' form.
There were a couple of notable cases:
- csky still had the old "verify_area()" name as an alias.
- the iter_iov code had magical hardcoded knowledge of the actual
values of VERIFY_{READ,WRITE} (not that they mattered, since nothing
really used it)
- microblaze used the type argument for a debug printout
but other than those oddities this should be a total no-op patch.
I tried to fix up all architectures, did fairly extensive grepping for
access_ok() uses, and the changes are trivial, but I may have missed
something. Any missed conversion should be trivially fixable, though.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2019-01-04 10:57:57 +08:00
|
|
|
if (!access_ok(frame, sizeof(*frame)))
|
2012-03-14 11:22:25 +08:00
|
|
|
goto badframe;
|
|
|
|
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
|
|
|
|
goto badframe;
|
|
|
|
|
2012-05-11 08:57:59 +08:00
|
|
|
set_current_blocked(&set);
|
2012-03-14 11:22:25 +08:00
|
|
|
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
size = rt_restore_ucontext(regs, sw, &frame->uc);
|
|
|
|
if (size < 0)
|
2012-03-14 11:22:25 +08:00
|
|
|
goto badframe;
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
return (void *)sw - size;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
badframe:
|
2019-05-23 23:17:27 +08:00
|
|
|
force_sig(SIGSEGV);
|
m68k: Leave stack mangling to asm wrapper of sigreturn()
sigreturn has to deal with an unpleasant problem - exception stack frames
have different sizes, depending upon the exception (and processor model, as
well) and variable-sized part of exception frame may contain information
needed for instruction restart. So when signal handler terminates and calls
sigreturn to resume the execution at the place where we'd been when we caught
the signal, it has to rearrange the frame at the bottom of kernel stack.
Worse, it might need to open a gap in the kernel stack, shifting pt_regs
towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses,
local variables, etc.) of C call chain, right under the nose of compiler and
hope it won't fall apart horribly. What had been actually done is only slightly
less insane - an inline asm in mangle_kernel_stack() moved the stuff around,
then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway
would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +
switch_stack, memcpy() the variable part of exception frame into the opened
gap - all of that without inline asm, buggering C call chain, magical jumps
to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up -
it might have been shifted into the gap we'd reserved before do_rt_sigreturn()
call. That's where it needs to set the stack pointer to. So let the C part
return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when
returning via 68040 bus error exception frame can be moved into C part
as well.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>
Tested-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/YP2dTQPm1wGPWFgD@zeniv-ca.linux.org.uk
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
2021-07-26 01:20:13 +08:00
|
|
|
return sw;
|
2012-03-14 11:22:25 +08:00
|
|
|
}
|
|
|
|
|
2021-07-26 01:19:00 +08:00
|
|
|
static inline struct pt_regs *rte_regs(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
return (void *)regs + regs->stkadj;
|
|
|
|
}
|
|
|
|
|
2012-03-14 11:22:25 +08:00
|
|
|
static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,
|
|
|
|
unsigned long mask)
|
|
|
|
{
|
2021-07-26 01:19:00 +08:00
|
|
|
struct pt_regs *tregs = rte_regs(regs);
|
2012-03-14 11:22:25 +08:00
|
|
|
sc->sc_mask = mask;
|
|
|
|
sc->sc_usp = rdusp();
|
|
|
|
sc->sc_d0 = regs->d0;
|
|
|
|
sc->sc_d1 = regs->d1;
|
|
|
|
sc->sc_a0 = regs->a0;
|
|
|
|
sc->sc_a1 = regs->a1;
|
2021-07-26 01:19:00 +08:00
|
|
|
sc->sc_sr = tregs->sr;
|
|
|
|
sc->sc_pc = tregs->pc;
|
|
|
|
sc->sc_formatvec = tregs->format << 12 | tregs->vector;
|
2012-03-14 11:22:25 +08:00
|
|
|
save_a5_state(sc, regs);
|
|
|
|
save_fpu_state(sc, regs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
struct switch_stack *sw = (struct switch_stack *)regs - 1;
|
2021-07-26 01:19:00 +08:00
|
|
|
struct pt_regs *tregs = rte_regs(regs);
|
2012-03-14 11:22:25 +08:00
|
|
|
greg_t __user *gregs = uc->uc_mcontext.gregs;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version);
|
|
|
|
err |= __put_user(regs->d0, &gregs[0]);
|
|
|
|
err |= __put_user(regs->d1, &gregs[1]);
|
|
|
|
err |= __put_user(regs->d2, &gregs[2]);
|
|
|
|
err |= __put_user(regs->d3, &gregs[3]);
|
|
|
|
err |= __put_user(regs->d4, &gregs[4]);
|
|
|
|
err |= __put_user(regs->d5, &gregs[5]);
|
|
|
|
err |= __put_user(sw->d6, &gregs[6]);
|
|
|
|
err |= __put_user(sw->d7, &gregs[7]);
|
|
|
|
err |= __put_user(regs->a0, &gregs[8]);
|
|
|
|
err |= __put_user(regs->a1, &gregs[9]);
|
|
|
|
err |= __put_user(regs->a2, &gregs[10]);
|
|
|
|
err |= __put_user(sw->a3, &gregs[11]);
|
|
|
|
err |= __put_user(sw->a4, &gregs[12]);
|
|
|
|
err |= __put_user(sw->a5, &gregs[13]);
|
|
|
|
err |= __put_user(sw->a6, &gregs[14]);
|
|
|
|
err |= __put_user(rdusp(), &gregs[15]);
|
2021-07-26 01:19:00 +08:00
|
|
|
err |= __put_user(tregs->pc, &gregs[16]);
|
|
|
|
err |= __put_user(tregs->sr, &gregs[17]);
|
|
|
|
err |= __put_user((tregs->format << 12) | tregs->vector, &uc->uc_formatvec);
|
2012-03-14 11:22:25 +08:00
|
|
|
err |= rt_save_fpu_state(uc, regs);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void __user *
|
2014-03-05 22:28:59 +08:00
|
|
|
get_sigframe(struct ksignal *ksig, size_t frame_size)
|
2012-03-14 11:22:25 +08:00
|
|
|
{
|
2014-03-05 22:28:59 +08:00
|
|
|
unsigned long usp = sigsp(rdusp(), ksig);
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
return (void __user *)((usp - frame_size) & -8UL);
|
|
|
|
}
|
|
|
|
|
2013-10-08 04:55:48 +08:00
|
|
|
static int setup_frame(struct ksignal *ksig, sigset_t *set,
|
|
|
|
struct pt_regs *regs)
|
2012-03-14 11:22:25 +08:00
|
|
|
{
|
|
|
|
struct sigframe __user *frame;
|
2021-07-26 01:19:00 +08:00
|
|
|
struct pt_regs *tregs = rte_regs(regs);
|
|
|
|
int fsize = frame_extra_sizes(tregs->format);
|
2012-03-14 11:22:25 +08:00
|
|
|
struct sigcontext context;
|
2013-10-08 04:55:48 +08:00
|
|
|
int err = 0, sig = ksig->sig;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
if (fsize < 0) {
|
2016-12-07 02:57:37 +08:00
|
|
|
pr_debug("setup_frame: Unknown frame format %#x\n",
|
2021-07-26 01:19:00 +08:00
|
|
|
tregs->format);
|
2013-10-08 04:55:48 +08:00
|
|
|
return -EFAULT;
|
2012-03-14 11:22:25 +08:00
|
|
|
}
|
|
|
|
|
2014-03-05 22:28:59 +08:00
|
|
|
frame = get_sigframe(ksig, sizeof(*frame) + fsize);
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
if (fsize)
|
|
|
|
err |= copy_to_user (frame + 1, regs + 1, fsize);
|
|
|
|
|
2014-07-13 23:19:34 +08:00
|
|
|
err |= __put_user(sig, &frame->sig);
|
2012-03-14 11:22:25 +08:00
|
|
|
|
2021-07-26 01:19:00 +08:00
|
|
|
err |= __put_user(tregs->vector, &frame->code);
|
2012-03-14 11:22:25 +08:00
|
|
|
err |= __put_user(&frame->sc, &frame->psc);
|
|
|
|
|
|
|
|
if (_NSIG_WORDS > 1)
|
|
|
|
err |= copy_to_user(frame->extramask, &set->sig[1],
|
|
|
|
sizeof(frame->extramask));
|
|
|
|
|
|
|
|
setup_sigcontext(&context, regs, set->sig[0]);
|
|
|
|
err |= copy_to_user (&frame->sc, &context, sizeof(context));
|
|
|
|
|
|
|
|
/* Set up to return from userspace. */
|
|
|
|
#ifdef CONFIG_MMU
|
|
|
|
err |= __put_user(frame->retcode, &frame->pretcode);
|
|
|
|
/* moveq #,d0; trap #0 */
|
|
|
|
err |= __put_user(0x70004e40 + (__NR_sigreturn << 16),
|
|
|
|
(long __user *)(frame->retcode));
|
|
|
|
#else
|
2020-09-15 13:16:49 +08:00
|
|
|
err |= __put_user((long) ret_from_user_signal,
|
|
|
|
(long __user *) &frame->pretcode);
|
2012-03-14 11:22:25 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (err)
|
2013-10-08 04:55:48 +08:00
|
|
|
return -EFAULT;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
push_cache ((unsigned long) &frame->retcode);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is subtle; if we build more than one sigframe, all but the
|
|
|
|
* first one will see frame format 0 and have fsize == 0, so we won't
|
|
|
|
* screw stkadj.
|
|
|
|
*/
|
2021-07-26 01:19:00 +08:00
|
|
|
if (fsize) {
|
2012-03-14 11:22:25 +08:00
|
|
|
regs->stkadj = fsize;
|
2021-07-26 01:19:00 +08:00
|
|
|
tregs = rte_regs(regs);
|
2016-12-07 02:57:37 +08:00
|
|
|
pr_debug("Performing stackadjust=%04lx\n", regs->stkadj);
|
2012-03-14 11:22:25 +08:00
|
|
|
tregs->vector = 0;
|
|
|
|
tregs->format = 0;
|
|
|
|
tregs->sr = regs->sr;
|
|
|
|
}
|
2021-07-26 01:19:00 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up registers for signal handler. All the state we are about
|
|
|
|
* to destroy is successfully copied to sigframe.
|
|
|
|
*/
|
|
|
|
wrusp ((unsigned long) frame);
|
|
|
|
tregs->pc = (unsigned long) ksig->ka.sa.sa_handler;
|
|
|
|
adjustformat(regs);
|
|
|
|
|
2012-03-14 11:22:25 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-08 04:55:48 +08:00
|
|
|
static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
|
|
|
|
struct pt_regs *regs)
|
2012-03-14 11:22:25 +08:00
|
|
|
{
|
|
|
|
struct rt_sigframe __user *frame;
|
2021-07-26 01:19:00 +08:00
|
|
|
struct pt_regs *tregs = rte_regs(regs);
|
|
|
|
int fsize = frame_extra_sizes(tregs->format);
|
2013-10-08 04:55:48 +08:00
|
|
|
int err = 0, sig = ksig->sig;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
if (fsize < 0) {
|
2016-12-07 02:57:37 +08:00
|
|
|
pr_debug("setup_frame: Unknown frame format %#x\n",
|
|
|
|
regs->format);
|
2013-10-08 04:55:48 +08:00
|
|
|
return -EFAULT;
|
2012-03-14 11:22:25 +08:00
|
|
|
}
|
|
|
|
|
2014-03-05 22:28:59 +08:00
|
|
|
frame = get_sigframe(ksig, sizeof(*frame));
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
if (fsize)
|
|
|
|
err |= copy_to_user (&frame->uc.uc_extra, regs + 1, fsize);
|
|
|
|
|
2014-07-13 23:19:34 +08:00
|
|
|
err |= __put_user(sig, &frame->sig);
|
2012-03-14 11:22:25 +08:00
|
|
|
err |= __put_user(&frame->info, &frame->pinfo);
|
|
|
|
err |= __put_user(&frame->uc, &frame->puc);
|
2013-10-08 04:55:48 +08:00
|
|
|
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
/* Create the ucontext. */
|
|
|
|
err |= __put_user(0, &frame->uc.uc_flags);
|
|
|
|
err |= __put_user(NULL, &frame->uc.uc_link);
|
2012-12-23 16:06:03 +08:00
|
|
|
err |= __save_altstack(&frame->uc.uc_stack, rdusp());
|
2012-03-14 11:22:25 +08:00
|
|
|
err |= rt_setup_ucontext(&frame->uc, regs);
|
|
|
|
err |= copy_to_user (&frame->uc.uc_sigmask, set, sizeof(*set));
|
|
|
|
|
|
|
|
/* Set up to return from userspace. */
|
|
|
|
#ifdef CONFIG_MMU
|
|
|
|
err |= __put_user(frame->retcode, &frame->pretcode);
|
|
|
|
#ifdef __mcoldfire__
|
|
|
|
/* movel #__NR_rt_sigreturn,d0; trap #0 */
|
|
|
|
err |= __put_user(0x203c0000, (long __user *)(frame->retcode + 0));
|
|
|
|
err |= __put_user(0x00004e40 + (__NR_rt_sigreturn << 16),
|
|
|
|
(long __user *)(frame->retcode + 4));
|
|
|
|
#else
|
|
|
|
/* moveq #,d0; notb d0; trap #0 */
|
|
|
|
err |= __put_user(0x70004600 + ((__NR_rt_sigreturn ^ 0xff) << 16),
|
|
|
|
(long __user *)(frame->retcode + 0));
|
|
|
|
err |= __put_user(0x4e40, (short __user *)(frame->retcode + 4));
|
|
|
|
#endif
|
|
|
|
#else
|
2020-09-15 13:16:49 +08:00
|
|
|
err |= __put_user((long) ret_from_user_rt_signal,
|
|
|
|
(long __user *) &frame->pretcode);
|
2012-03-14 11:22:25 +08:00
|
|
|
#endif /* CONFIG_MMU */
|
|
|
|
|
|
|
|
if (err)
|
2013-10-08 04:55:48 +08:00
|
|
|
return -EFAULT;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
push_cache ((unsigned long) &frame->retcode);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is subtle; if we build more than one sigframe, all but the
|
|
|
|
* first one will see frame format 0 and have fsize == 0, so we won't
|
|
|
|
* screw stkadj.
|
|
|
|
*/
|
2021-07-26 01:19:00 +08:00
|
|
|
if (fsize) {
|
2012-03-14 11:22:25 +08:00
|
|
|
regs->stkadj = fsize;
|
2021-07-26 01:19:00 +08:00
|
|
|
tregs = rte_regs(regs);
|
2016-12-07 02:57:37 +08:00
|
|
|
pr_debug("Performing stackadjust=%04lx\n", regs->stkadj);
|
2012-03-14 11:22:25 +08:00
|
|
|
tregs->vector = 0;
|
|
|
|
tregs->format = 0;
|
|
|
|
tregs->sr = regs->sr;
|
|
|
|
}
|
2021-07-26 01:19:00 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up registers for signal handler. All the state we are about
|
|
|
|
* to destroy is successfully copied to sigframe.
|
|
|
|
*/
|
|
|
|
wrusp ((unsigned long) frame);
|
|
|
|
tregs->pc = (unsigned long) ksig->ka.sa.sa_handler;
|
|
|
|
adjustformat(regs);
|
2012-03-14 11:22:25 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler)
|
|
|
|
{
|
|
|
|
switch (regs->d0) {
|
|
|
|
case -ERESTARTNOHAND:
|
|
|
|
if (!has_handler)
|
|
|
|
goto do_restart;
|
|
|
|
regs->d0 = -EINTR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case -ERESTART_RESTARTBLOCK:
|
|
|
|
if (!has_handler) {
|
|
|
|
regs->d0 = __NR_restart_syscall;
|
|
|
|
regs->pc -= 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
regs->d0 = -EINTR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case -ERESTARTSYS:
|
|
|
|
if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) {
|
|
|
|
regs->d0 = -EINTR;
|
|
|
|
break;
|
|
|
|
}
|
2020-08-24 06:36:59 +08:00
|
|
|
fallthrough;
|
2012-03-14 11:22:25 +08:00
|
|
|
case -ERESTARTNOINTR:
|
|
|
|
do_restart:
|
|
|
|
regs->d0 = regs->orig_d0;
|
|
|
|
regs->pc -= 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, we're invoking a handler
|
|
|
|
*/
|
|
|
|
static void
|
2013-10-08 04:55:48 +08:00
|
|
|
handle_signal(struct ksignal *ksig, struct pt_regs *regs)
|
2012-03-14 11:22:25 +08:00
|
|
|
{
|
2012-05-02 21:59:21 +08:00
|
|
|
sigset_t *oldset = sigmask_to_save();
|
2012-03-14 11:22:25 +08:00
|
|
|
int err;
|
|
|
|
/* are we from a system call? */
|
|
|
|
if (regs->orig_d0 >= 0)
|
|
|
|
/* If so, check system call restarting.. */
|
2013-10-08 04:55:48 +08:00
|
|
|
handle_restart(regs, &ksig->ka, 1);
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
/* set up the stack frame */
|
2013-10-08 04:55:48 +08:00
|
|
|
if (ksig->ka.sa.sa_flags & SA_SIGINFO)
|
|
|
|
err = setup_rt_frame(ksig, oldset, regs);
|
2012-03-14 11:22:25 +08:00
|
|
|
else
|
2013-10-08 04:55:48 +08:00
|
|
|
err = setup_frame(ksig, oldset, regs);
|
2012-03-14 11:22:25 +08:00
|
|
|
|
2013-10-08 04:55:48 +08:00
|
|
|
signal_setup_done(err, ksig, 0);
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
if (test_thread_flag(TIF_DELAYED_TRACE)) {
|
|
|
|
regs->sr &= ~0x8000;
|
|
|
|
send_sig(SIGTRAP, current, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that 'init' is a special process: it doesn't get signals it doesn't
|
|
|
|
* want to handle. Thus you cannot kill init even with a SIGKILL even by
|
|
|
|
* mistake.
|
|
|
|
*/
|
2012-04-24 13:46:23 +08:00
|
|
|
static void do_signal(struct pt_regs *regs)
|
2012-03-14 11:22:25 +08:00
|
|
|
{
|
2013-10-08 04:55:48 +08:00
|
|
|
struct ksignal ksig;
|
2012-03-14 11:22:25 +08:00
|
|
|
|
|
|
|
current->thread.esp0 = (unsigned long) regs;
|
|
|
|
|
2013-10-08 04:55:48 +08:00
|
|
|
if (get_signal(&ksig)) {
|
2012-03-14 11:22:25 +08:00
|
|
|
/* Whee! Actually deliver the signal. */
|
2013-10-08 04:55:48 +08:00
|
|
|
handle_signal(&ksig, regs);
|
2012-03-14 11:22:25 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Did we come from a system call? */
|
|
|
|
if (regs->orig_d0 >= 0)
|
|
|
|
/* Restart the system call - no handlers present */
|
|
|
|
handle_restart(regs, NULL, 0);
|
|
|
|
|
|
|
|
/* If there's no signal to deliver, we just restore the saved mask. */
|
2012-05-22 11:33:55 +08:00
|
|
|
restore_saved_sigmask();
|
2012-03-14 11:22:25 +08:00
|
|
|
}
|
2012-04-24 13:46:23 +08:00
|
|
|
|
|
|
|
void do_notify_resume(struct pt_regs *regs)
|
|
|
|
{
|
2020-10-10 05:10:55 +08:00
|
|
|
if (test_thread_flag(TIF_NOTIFY_SIGNAL) ||
|
|
|
|
test_thread_flag(TIF_SIGPENDING))
|
2012-04-24 13:46:23 +08:00
|
|
|
do_signal(regs);
|
|
|
|
|
2020-10-04 00:49:22 +08:00
|
|
|
if (test_thread_flag(TIF_NOTIFY_RESUME))
|
2022-02-10 02:20:45 +08:00
|
|
|
resume_user_mode_work(regs);
|
2012-04-24 13:46:23 +08:00
|
|
|
}
|