patch-2.1.87 linux/arch/m68k/kernel/signal.c
Next file: linux/arch/m68k/kernel/sys_m68k.c
Previous file: linux/arch/m68k/kernel/setup.c
Back to the patch index
Back to the overall index
- Lines: 1178
- Date:
Thu Feb 12 16:30:12 1998
- Orig file:
v2.1.86/linux/arch/m68k/kernel/signal.c
- Orig date:
Sat Aug 16 09:51:07 1997
diff -u --recursive --new-file v2.1.86/linux/arch/m68k/kernel/signal.c linux/arch/m68k/kernel/signal.c
@@ -12,13 +12,15 @@
* Linux/m68k support by Hamish Macdonald
*
* 68060 fixes by Jesper Skov
+ *
+ * 1997-12-01 Modified for POSIX.1b signals by Andreas Schwab
*/
/*
* ++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_STACK set arrives while working on a sigstack,
- * SA_STACK is ignored. This behaviour avoids lots of trouble with nested
+ * 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!
*/
@@ -30,22 +32,19 @@
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <linux/unistd.h>
+#include <linux/stddef.h>
#include <asm/setup.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/traps.h>
+#include <asm/ucontext.h>
-#define offsetof(type, member) ((size_t)(&((type *)0)->member))
-
-#define _S(nr) (1<<((nr)-1))
-
-#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
int options, unsigned long *ru);
-
-asmlinkage int do_signal(unsigned long oldmask, struct pt_regs *regs);
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs);
const int frame_extra_sizes[16] = {
0,
@@ -67,54 +66,232 @@
};
/*
- * atomically swap in the new signal mask, and wait for a signal.
+ * Atomically swap in the new signal mask, and wait for a signal.
*/
asmlinkage int do_sigsuspend(struct pt_regs *regs)
{
- unsigned long oldmask = current->blocked;
- unsigned long newmask = regs->d3;
+ old_sigset_t mask = regs->d3;
+ sigset_t saveset;
+
+ mask &= _BLOCKABLE;
+ saveset = current->blocked;
+ siginitset(¤t->blocked, mask);
+ recalc_sigpending(current);
+
+ regs->d0 = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(&saveset, regs))
+ return -EINTR;
+ }
+}
+
+asmlinkage int
+do_rt_sigsuspend(struct pt_regs *regs)
+{
+ sigset_t *unewset = (sigset_t *)regs->d1;
+ size_t sigsetsize = (size_t)regs->d2;
+ sigset_t saveset, newset;
+
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(sigset_t))
+ return -EINVAL;
+
+ if (copy_from_user(&newset, unewset, sizeof(newset)))
+ return -EFAULT;
+ sigdelsetmask(&newset, ~_BLOCKABLE);
+
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending(current);
- current->blocked = newmask & _BLOCKABLE;
regs->d0 = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
- if (do_signal(oldmask, regs))
+ if (do_signal(&saveset, regs))
return -EINTR;
}
}
+asmlinkage int
+sys_sigaction(int sig, const struct old_sigaction *act,
+ struct old_sigaction *oact)
+{
+ struct k_sigaction new_ka, old_ka;
+ int ret;
+
+ if (act) {
+ old_sigset_t mask;
+ if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
+ __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+ __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+ return -EFAULT;
+ __get_user(new_ka.sa.sa_flags, &act->sa_flags);
+ __get_user(mask, &act->sa_mask);
+ siginitset(&new_ka.sa.sa_mask, mask);
+ }
+
+ ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+ if (!ret && oact) {
+ if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
+ __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+ __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+ return -EFAULT;
+ __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+ }
+
+ return ret;
+}
+
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+
+struct sigframe
+{
+ char *pretcode;
+ int sig;
+ int code;
+ struct sigcontext *psc;
+ char retcode[8];
+ unsigned long extramask[_NSIG_WORDS-1];
+ struct sigcontext sc;
+};
+
+struct rt_sigframe
+{
+ char *pretcode;
+ int sig;
+ struct siginfo *pinfo;
+ void *puc;
+ struct siginfo info;
+ struct ucontext uc;
+ char retcode[8];
+};
+
+
static unsigned char fpu_version = 0; /* version number of fpu, set by setup_frame */
-asmlinkage int do_sigreturn(unsigned long __unused)
+static inline void restore_fpu_state(struct sigcontext *sc)
{
- struct sigcontext context;
- struct pt_regs *regs;
- struct switch_stack *sw;
- int fsize = 0;
- int formatvec = 0;
- unsigned long fp;
- unsigned long usp = rdusp();
+ if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) {
+ /* Verify the frame format. */
+ if (!CPU_IS_060 && (sc->sc_fpstate[0] != fpu_version))
+ goto badframe;
+ if (CPU_IS_020_OR_030) {
+ if (m68k_fputype & FPU_68881 &&
+ !(sc->sc_fpstate[1] == 0x18 || sc->sc_fpstate[1] == 0xb4))
+ goto badframe;
+ if (m68k_fputype & FPU_68882 &&
+ !(sc->sc_fpstate[1] == 0x38 || sc->sc_fpstate[1] == 0xd4))
+ goto badframe;
+ } else if (CPU_IS_040) {
+ if (!(sc->sc_fpstate[1] == 0x00 ||
+ sc->sc_fpstate[1] == 0x28 ||
+ sc->sc_fpstate[1] == 0x60))
+ goto badframe;
+ } else if (CPU_IS_060) {
+ if (!(sc->sc_fpstate[3] == 0x00 ||
+ sc->sc_fpstate[3] == 0x60 ||
+ sc->sc_fpstate[3] == 0xe0))
+ goto badframe;
+ } else
+ goto badframe;
-#if 0
- printk("sys_sigreturn, usp=%08x\n", (unsigned) usp);
-#endif
+ __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));
+ }
+ __asm__ volatile (".chip 68k/68881\n\t"
+ "frestore %0\n\t"
+ ".chip 68k" : : "m" (*sc->sc_fpstate));
+ return;
+
+badframe:
+ do_exit(SIGSEGV);
+}
- /* get stack frame pointer */
- sw = (struct switch_stack *) &__unused;
- regs = (struct pt_regs *) (sw + 1);
+#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]
- /* get previous context (including pointer to possible extra junk) */
- if (copy_from_user(&context,(void *)usp, sizeof(context)))
+static inline void rt_restore_fpu_state(struct ucontext *uc)
+{
+ unsigned char fpstate[FPCONTEXT_SIZE];
+ int context_size = CPU_IS_060 ? 8 : 0;
+ fpregset_t fpregs;
+
+ if (__get_user(*(long *)fpstate, (long *)&uc->uc_fpstate))
+ goto badframe;
+ if (CPU_IS_060 ? fpstate[2] : fpstate[0]) {
+ if (!CPU_IS_060)
+ context_size = fpstate[1];
+ /* Verify the frame format. */
+ if (!CPU_IS_060 && (fpstate[0] != fpu_version))
+ goto badframe;
+ if (CPU_IS_020_OR_030) {
+ if (m68k_fputype & FPU_68881 &&
+ !(context_size == 0x18 || context_size == 0xb4))
+ goto badframe;
+ if (m68k_fputype & FPU_68882 &&
+ !(context_size == 0x38 || context_size == 0xd4))
+ goto badframe;
+ } else if (CPU_IS_040) {
+ if (!(context_size == 0x00 ||
+ context_size == 0x28 ||
+ context_size == 0x60))
+ goto badframe;
+ } else if (CPU_IS_060) {
+ if (!(fpstate[3] == 0x00 ||
+ fpstate[3] == 0x60 ||
+ fpstate[3] == 0xe0))
+ goto badframe;
+ } else
+ goto badframe;
+ if (__copy_from_user(&fpregs, &uc->uc_mcontext.fpregs,
+ sizeof(fpregs)))
+ goto badframe;
+ __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_pcr));
+ }
+ if (context_size &&
+ __copy_from_user(fpstate + 4, (long *)&uc->uc_fpstate + 1,
+ context_size))
goto badframe;
+ __asm__ volatile (".chip 68k/68881\n\t"
+ "frestore %0\n\t"
+ ".chip 68k" : : "m" (*fpstate));
+ return;
- fp = usp + sizeof (context);
+badframe:
+ do_exit(SIGSEGV);
+}
- /* restore signal mask */
- current->blocked = context.sc_mask & _BLOCKABLE;
+static inline int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext *usc, void *fp)
+{
+ int fsize, formatvec;
+ struct sigcontext context;
+ /* get previous context */
+ if (copy_from_user(&context, usc, sizeof(context)))
+ goto badframe;
+
/* restore passed registers */
- regs->d0 = context.sc_d0;
regs->d1 = context.sc_d1;
regs->a0 = context.sc_a0;
regs->a1 = context.sc_a1;
@@ -125,43 +302,8 @@
formatvec = context.sc_formatvec;
regs->format = formatvec >> 12;
regs->vector = formatvec & 0xfff;
- if ((!CPU_IS_060 && context.sc_fpstate[0]) || (CPU_IS_060 && context.sc_fpstate[2])){
- /* Verify the frame format. */
- if (!CPU_IS_060 && (context.sc_fpstate[0] != fpu_version))
- goto badframe;
- if (m68k_fputype & FPU_68881)
- {
- if (context.sc_fpstate[1] != 0x18
- && context.sc_fpstate[1] != 0xb4)
- goto badframe;
- }
- else if (m68k_fputype & FPU_68882)
- {
- if (context.sc_fpstate[1] != 0x38
- && context.sc_fpstate[1] != 0xd4)
- goto badframe;
- }
- else if (m68k_fputype & FPU_68040)
- {
- if (!(context.sc_fpstate[1] == 0x00 ||
- context.sc_fpstate[1] == 0x28 ||
- context.sc_fpstate[1] == 0x60))
- goto badframe;
- }
- else if (m68k_fputype & FPU_68060)
- {
- if (!(context.sc_fpstate[3] == 0x00 ||
- context.sc_fpstate[3] == 0x60 ||
- context.sc_fpstate[3] == 0xe0))
- goto badframe;
- }
- __asm__ volatile ("fmovemx %0,%/fp0-%/fp1\n\t"
- "fmoveml %1,%/fpcr/%/fpsr/%/fpiar"
- : /* no outputs */
- : "m" (*context.sc_fpregs),
- "m" (*context.sc_fpcntl));
- }
- __asm__ volatile ("frestore %0" : : "m" (*context.sc_fpstate));
+
+ restore_fpu_state(&context);
fsize = frame_extra_sizes[regs->format];
if (fsize < 0) {
@@ -174,14 +316,13 @@
goto badframe;
}
- if (context.sc_usp != fp+fsize)
- current->flags &= ~PF_ONSIGSTK;
-
/* OK. Make room on the supervisor stack for the extra junk,
* if necessary.
*/
if (fsize) {
+ struct switch_stack *sw = (struct switch_stack *)regs - 1;
+ regs->d0 = context.sc_d0;
#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack))
__asm__ __volatile__
(" movel %0,%/a0\n\t"
@@ -215,127 +356,494 @@
goto badframe;
}
+ return context.sc_d0;
+
+badframe:
+ do_exit(SIGSEGV);
+}
+
+static inline int
+rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw,
+ struct ucontext *uc)
+{
+ int fsize, temp;
+ greg_t *gregs = uc->uc_mcontext.gregs;
+
+ __get_user(temp, &uc->uc_mcontext.version);
+ if (temp != MCONTEXT_VERSION)
+ goto badframe;
+ /* restore passed registers */
+ __get_user(regs->d0, &gregs[0]);
+ __get_user(regs->d1, &gregs[1]);
+ __get_user(regs->d2, &gregs[2]);
+ __get_user(regs->d3, &gregs[3]);
+ __get_user(regs->d4, &gregs[4]);
+ __get_user(regs->d5, &gregs[5]);
+ __get_user(sw->d6, &gregs[6]);
+ __get_user(sw->d7, &gregs[7]);
+ __get_user(regs->a0, &gregs[8]);
+ __get_user(regs->a1, &gregs[9]);
+ __get_user(regs->a2, &gregs[10]);
+ __get_user(sw->a3, &gregs[11]);
+ __get_user(sw->a4, &gregs[12]);
+ __get_user(sw->a5, &gregs[13]);
+ __get_user(sw->a6, &gregs[14]);
+ __get_user(temp, &gregs[15]);
+ wrusp(temp);
+ __get_user(regs->pc, &gregs[16]);
+ __get_user(temp, &gregs[17]);
+ regs->sr = (regs->sr & 0xff00) | (temp & 0xff);
+ regs->orig_d0 = -1; /* disable syscall checks */
+ __get_user(temp, &uc->uc_formatvec);
+ regs->format = temp >> 12;
+ regs->vector = temp & 0xfff;
+
+ rt_restore_fpu_state(uc);
+
+ fsize = frame_extra_sizes[regs->format];
+ if (fsize < 0) {
+ /*
+ * user process trying to return with weird frame format
+ */
+#if DEBUG
+ printk("user process returning with weird frame format\n");
+#endif
+ goto badframe;
+ }
+
+ /* OK. Make room on the supervisor stack for the extra junk,
+ * if necessary.
+ */
+
+ if (fsize) {
+#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack))
+ __asm__ __volatile__
+ (" movel %0,%/a0\n\t"
+ " subl %1,%/a0\n\t" /* make room on stack */
+ " movel %/a0,%/sp\n\t" /* set stack pointer */
+ /* move switch_stack and pt_regs */
+ "1: movel %0@+,%/a0@+\n\t"
+ " dbra %2,1b\n\t"
+ " lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */
+ " lsrl #2,%1\n\t"
+ " subql #1,%1\n\t"
+ "2: movesl %4@+,%2\n\t"
+ "3: movel %2,%/a0@+\n\t"
+ " dbra %1,2b\n\t"
+ " bral " SYMBOL_NAME_STR(ret_from_signal) "\n"
+ "4:\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 4\n"
+ " .long 2b,4b\n"
+ " .long 3b,4b\n"
+ ".previous"
+ : /* no outputs, it doesn't ever return */
+ : "a" (sw), "d" (fsize), "d" (frame_offset/4-1),
+ "n" (frame_offset), "a" (&uc->uc_extra)
+ : "a0");
+#undef frame_offset
+ /*
+ * If we ever get here an exception occured while
+ * building the above stack-frame.
+ */
+ goto badframe;
+ }
+
return regs->d0;
+
badframe:
do_exit(SIGSEGV);
}
+asmlinkage int do_sigreturn(unsigned long __unused)
+{
+ struct switch_stack *sw = (struct switch_stack *) &__unused;
+ struct pt_regs *regs = (struct pt_regs *) (sw + 1);
+ unsigned long usp = rdusp();
+ struct sigframe *frame = (struct sigframe *)(usp - 24);
+ sigset_t set;
+
+ if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+ 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;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ current->blocked = set;
+ recalc_sigpending(current);
+
+ return restore_sigcontext(regs, &frame->sc, frame + 1);
+
+badframe:
+ do_exit(SIGSEGV);
+}
+
+asmlinkage int do_rt_sigreturn(unsigned long __unused)
+{
+ struct switch_stack *sw = (struct switch_stack *) &__unused;
+ struct pt_regs *regs = (struct pt_regs *) (sw + 1);
+ unsigned long usp = rdusp();
+ struct rt_sigframe *frame = (struct rt_sigframe *)(usp - 4);
+ sigset_t set;
+
+ if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ current->blocked = set;
+ recalc_sigpending(current);
+
+ return rt_restore_ucontext(regs, sw, &frame->uc);
+
+badframe:
+ do_exit(SIGSEGV);
+}
+
/*
- * Set up a signal frame...
- *
- * This routine is somewhat complicated by the fact that if the
- * kernel may be entered by an exception other than a system call;
- * e.g. a bus error or other "bad" exception. If this is the case,
- * then *all* the context on the kernel stack frame must be saved.
- *
- * For a large number of exceptions, the stack frame format is the same
- * as that which will be created when the process traps back to the kernel
- * when finished executing the signal handler. In this case, nothing
- * must be done. This is exception frame format "0". For exception frame
- * formats "2", "9", "A" and "B", the extra information on the frame must
- * be saved. This information is saved on the user stack and restored
- * when the signal handler is returned.
- *
- * The format of the user stack when executing the signal handler is:
- *
- * usp -> RETADDR (points to code below)
- * signum (parm #1)
- * sigcode (parm #2 ; vector number)
- * scp (parm #3 ; sigcontext pointer, pointer to #1 below)
- * code1 (addaw #20,sp) ; pop parms and code off stack
- * code2 (moveq #119,d0; trap #0) ; sigreturn syscall
- * #1| oldmask
- * | old usp
- * | d0 (first saved reg)
- * | d1
- * | a0
- * | a1
- * | sr (saved status register)
- * | pc (old pc; one to return to)
- * | forvec (format and vector word of old supervisor stack frame)
- * | floating point context
- *
- * These are optionally followed by some extra stuff, depending on the
- * stack frame interrupted. This is 1 longword for format "2", 3
- * longwords for format "9", 6 longwords for format "A", and 21
- * longwords for format "B".
+ * Set up a signal frame.
*/
-#define UFRAME_SIZE(fs) (sizeof(struct sigcontext)/4 + 6 + fs/4)
+static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs)
+{
+ __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];
+ if (CPU_IS_020_OR_030 &&
+ 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;
+ }
+ __asm__ volatile (".chip 68k/68881\n\t"
+ "fmovemx %/fp0-%/fp1,%0\n\t"
+ "fmoveml %/fpcr/%/fpsr/%/fpiar,%1\n\t"
+ ".chip 68k"
+ : /* no outputs */
+ : "m" (*sc->sc_fpregs),
+ "m" (*sc->sc_fpcntl)
+ : "memory");
+ }
+}
-static inline void setup_frame (struct sigaction * sa, struct pt_regs *regs,
- int signr, unsigned long oldmask)
+static inline void rt_save_fpu_state(struct ucontext *uc, struct pt_regs *regs)
{
- struct sigcontext context;
- unsigned long *frame, *tframe;
+ unsigned char fpstate[FPCONTEXT_SIZE];
+ int context_size = CPU_IS_060 ? 8 : 0;
+
+ __asm__ volatile (".chip 68k/68881\n\t"
+ "fsave %0\n\t"
+ ".chip 68k"
+ : : "m" (*fpstate) : "memory");
+
+ __put_user(*(long *)fpstate, (long *)&uc->uc_fpstate);
+ if (CPU_IS_060 ? fpstate[2] : fpstate[0]) {
+ fpregset_t fpregs;
+ if (!CPU_IS_060)
+ context_size = fpstate[1];
+ fpu_version = fpstate[0];
+ if (CPU_IS_020_OR_030 &&
+ 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;
+ }
+ __asm__ volatile (".chip 68k/68881\n\t"
+ "fmovemx %/fp0-%/fp7,%0\n\t"
+ "fmoveml %/fpcr/%/fpsr/%/fpiar,%1\n\t"
+ ".chip 68k"
+ : /* no outputs */
+ : "m" (*fpregs.f_fpregs),
+ "m" (fpregs.f_pcr)
+ : "memory");
+ copy_to_user(&uc->uc_mcontext.fpregs, &fpregs, sizeof(fpregs));
+ }
+ if (context_size)
+ copy_to_user((long *)&uc->uc_fpstate + 1, fpstate + 4,
+ context_size);
+}
+
+static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,
+ unsigned long mask)
+{
+ 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;
+ sc->sc_sr = regs->sr;
+ sc->sc_pc = regs->pc;
+ sc->sc_formatvec = regs->format << 12 | regs->vector;
+ save_fpu_state(sc, regs);
+}
+
+static inline void rt_setup_ucontext(struct ucontext *uc, struct pt_regs *regs)
+{
+ struct switch_stack *sw = (struct switch_stack *)regs - 1;
+ greg_t *gregs = uc->uc_mcontext.gregs;
+
+ __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version);
+ __put_user(regs->d0, &gregs[0]);
+ __put_user(regs->d1, &gregs[1]);
+ __put_user(regs->d2, &gregs[2]);
+ __put_user(regs->d3, &gregs[3]);
+ __put_user(regs->d4, &gregs[4]);
+ __put_user(regs->d5, &gregs[5]);
+ __put_user(sw->d6, &gregs[6]);
+ __put_user(sw->d7, &gregs[7]);
+ __put_user(regs->a0, &gregs[8]);
+ __put_user(regs->a1, &gregs[9]);
+ __put_user(regs->a2, &gregs[10]);
+ __put_user(sw->a3, &gregs[11]);
+ __put_user(sw->a4, &gregs[12]);
+ __put_user(sw->a5, &gregs[13]);
+ __put_user(sw->a6, &gregs[14]);
+ __put_user(rdusp(), &gregs[15]);
+ __put_user(regs->pc, &gregs[16]);
+ __put_user(regs->sr, &gregs[17]);
+ __put_user((regs->format << 12) | regs->vector, &uc->uc_formatvec);
+ rt_save_fpu_state(uc, regs);
+}
+
+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));
+ if (((vaddr + 8) ^ vaddr) & ~15) {
+ if (((vaddr + 8) ^ vaddr) & PAGE_MASK)
+ __asm__ __volatile__ (".chip 68040\n\t"
+ "nop\n\t"
+ "ptestr (%1)\n\t"
+ "movec %%mmusr,%0\n\t"
+ ".chip 68k"
+ : "=r" (temp)
+ : "a" (vaddr + 8));
+
+ temp &= PAGE_MASK;
+ temp |= (vaddr + 8) & ~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));
+ if (((vaddr + 8) ^ vaddr) & ~15) {
+ if (((vaddr + 8) ^ vaddr) & PAGE_MASK)
+ __asm__ __volatile__ (".chip 68060\n\t"
+ "plpar (%0)\n\t"
+ ".chip 68k"
+ : "=a" (temp)
+ : "0" (vaddr + 8));
+ __asm__ __volatile__ (".chip 68060\n\t"
+ "cpushl %%bc,(%0)\n\t"
+ ".chip 68k"
+ : : "a" (temp));
+ }
+ }
+ else {
+ /*
+ * 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));
+ }
+}
+
+static void setup_frame (int sig, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct sigframe *frame;
int fsize = frame_extra_sizes[regs->format];
+ struct sigcontext context;
if (fsize < 0) {
+#ifdef DEBUG
printk ("setup_frame: Unknown frame format %#x\n",
regs->format);
- do_exit(SIGSEGV);
+#endif
+ goto segv_and_exit;
}
- frame = (unsigned long *)rdusp();
- if (!(current->flags & PF_ONSIGSTK) && (sa->sa_flags & SA_STACK)) {
- frame = (unsigned long *)sa->sa_restorer;
+ frame = (struct sigframe *)((rdusp() - sizeof(*frame) - fsize) & -8);
+
+ if (!(current->flags & PF_ONSIGSTK) && (ka->sa.sa_flags & SA_ONSTACK)) {
+ frame = (struct sigframe *)(((unsigned long)ka->sa.sa_restorer
+ - sizeof(*frame) - fsize) & -8);
current->flags |= PF_ONSIGSTK;
}
- frame -= UFRAME_SIZE(fsize);
if (fsize) {
- if (copy_to_user (frame + UFRAME_SIZE(0), regs + 1, fsize))
- do_exit(SIGSEGV);
+ if (copy_to_user (frame + 1, regs + 1, fsize))
+ goto segv_and_exit;
regs->stkadj = fsize;
}
-/* set up the "normal" stack seen by the signal handler */
- tframe = frame;
+ __put_user((current->exec_domain
+ && current->exec_domain->signal_invmap
+ && sig < 32
+ ? current->exec_domain->signal_invmap[sig]
+ : sig),
+ &frame->sig);
+
+ __put_user(regs->vector, &frame->code);
+ __put_user(&frame->sc, &frame->psc);
+
+ if (_NSIG_WORDS > 1)
+ copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask));
+
+ setup_sigcontext(&context, regs, set->sig[0]);
+ if (copy_to_user (&frame->sc, &context, sizeof(context)))
+ goto segv_and_exit;
+
+ /* Set up to return from userspace. */
+ __put_user(frame->retcode, &frame->pretcode);
+ /* addaw #20,sp */
+ __put_user(0xdefc0014, (long *)(frame->retcode + 0));
+ /* moveq #,d0; trap #0 */
+ __put_user(0x70004e40 + (__NR_sigreturn << 16),
+ (long *)(frame->retcode + 4));
- /* return address points to code on stack */
+ push_cache ((unsigned long) &frame->retcode);
- if(put_user((ulong)(frame+4), tframe))
- do_exit(SIGSEGV);
- tframe++;
- if (current->exec_domain && current->exec_domain->signal_invmap)
- __put_user(current->exec_domain->signal_invmap[signr], tframe);
- else
- __put_user(signr, tframe);
- tframe++;
+ /*
+ * no matter what frame format we were using before, we
+ * will do the "RTE" using a normal 4 word frame.
+ */
+ regs->format = 0;
- __put_user(regs->vector, tframe); tframe++;
- /* "scp" parameter. points to sigcontext */
- __put_user((ulong)(frame+6), tframe); tframe++;
-
-/* set up the return code... */
- __put_user(0xdefc0014,tframe); tframe++; /* addaw #20,sp */
- __put_user(0x70774e40,tframe); tframe++; /* moveq #119,d0; trap #0 */
-
-/* Flush caches so the instructions will be correctly executed. (MA) */
- cache_push_v ((unsigned long)frame, (int)tframe - (int)frame);
-
-/* setup and copy the sigcontext structure */
- context.sc_mask = oldmask;
- context.sc_usp = rdusp();
- context.sc_d0 = regs->d0;
- context.sc_d1 = regs->d1;
- context.sc_a0 = regs->a0;
- context.sc_a1 = regs->a1;
- context.sc_sr = regs->sr;
- context.sc_pc = regs->pc;
- context.sc_formatvec = (regs->format << 12 | regs->vector);
- __asm__ volatile ("fsave %0" : : "m" (*context.sc_fpstate) : "memory");
- if ((!CPU_IS_060 && context.sc_fpstate[0]) || (CPU_IS_060 && context.sc_fpstate[2])){
- fpu_version = context.sc_fpstate[0];
- __asm__ volatile ("fmovemx %/fp0-%/fp1,%0\n\t"
- "fmoveml %/fpcr/%/fpsr/%/fpiar,%1"
- : /* no outputs */
- : "m" (*context.sc_fpregs),
- "m" (*context.sc_fpcntl)
- : "memory");
+ /* Set up registers for signal handler */
+ wrusp ((unsigned long) frame);
+ regs->pc = (unsigned long) ka->sa.sa_handler;
+
+ /* Prepare to skip over the extra stuff in the exception frame. */
+ if (regs->stkadj) {
+ struct pt_regs *tregs =
+ (struct pt_regs *)((ulong)regs + regs->stkadj);
+#if DEBUG
+ printk("Performing stackadjust=%04x\n", regs->stkadj);
+#endif
+ /* This must be copied with decreasing addresses to
+ handle overlaps. */
+ tregs->vector = regs->vector;
+ tregs->format = regs->format;
+ tregs->pc = regs->pc;
+ tregs->sr = regs->sr;
}
- if (copy_to_user (tframe, &context, sizeof(context)))
- do_exit(SIGSEGV);
+ return;
+
+segv_and_exit:
+ do_exit(SIGSEGV);
+}
+
+static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct rt_sigframe *frame;
+ int fsize = frame_extra_sizes[regs->format];
+
+ if (fsize < 0) {
+#ifdef DEBUG
+ printk ("setup_frame: Unknown frame format %#x\n",
+ regs->format);
+#endif
+ goto segv_and_exit;
+ }
+
+ frame = (struct rt_sigframe *)((rdusp() - sizeof(*frame)) & -8);
+
+ /* XXX: Check here if we need to switch stacks.. */
+
+ if (fsize) {
+ if (copy_to_user (&frame->uc.uc_extra, regs + 1, fsize))
+ goto segv_and_exit;
+ regs->stkadj = fsize;
+ }
+
+ __put_user((current->exec_domain
+ && current->exec_domain->signal_invmap
+ && sig < 32
+ ? current->exec_domain->signal_invmap[sig]
+ : sig),
+ &frame->sig);
+ __put_user(&frame->info, &frame->pinfo);
+ __put_user(&frame->uc, &frame->puc);
+ __copy_to_user(&frame->info, info, sizeof(*info));
+
+ /* Clear all the bits of the ucontext we don't use. */
+ clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
+
+ rt_setup_ucontext(&frame->uc, regs);
+ if (copy_to_user (&frame->uc.uc_sigmask, set, sizeof(*set)))
+ goto segv_and_exit;
+
+ /* Set up to return from userspace. */
+ __put_user(frame->retcode, &frame->pretcode);
+ /* movel #,d0; trap #0 */
+ __put_user(0x203c, (short *)(frame->retcode + 0));
+ __put_user(__NR_rt_sigreturn, (long *)(frame->retcode + 2));
+ __put_user(0x4e40, (short *)(frame->retcode + 6));
+
+ push_cache ((unsigned long) &frame->retcode);
/*
* no matter what frame format we were using before, we
@@ -345,7 +853,7 @@
/* Set up registers for signal handler */
wrusp ((unsigned long) frame);
- regs->pc = (unsigned long) sa->sa_handler;
+ regs->pc = (unsigned long) ka->sa.sa_handler;
/* Prepare to skip over the extra stuff in the exception frame. */
if (regs->stkadj) {
@@ -361,13 +869,18 @@
tregs->pc = regs->pc;
tregs->sr = regs->sr;
}
+ return;
+
+segv_and_exit:
+ do_exit(SIGSEGV);
}
/*
* OK, we're invoking a handler
*/
-static inline void handle_signal(unsigned long signr, struct sigaction *sa,
- unsigned long oldmask, struct pt_regs *regs)
+static void
+handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *oldset, struct pt_regs *regs)
{
/* are we from a system call? */
if (regs->orig_d0 >= 0) {
@@ -378,7 +891,7 @@
break;
case -ERESTARTSYS:
- if (!(sa->sa_flags & SA_RESTART)) {
+ if (!(ka->sa.sa_flags & SA_RESTART)) {
regs->d0 = -EINTR;
break;
}
@@ -390,12 +903,19 @@
}
/* set up the stack frame */
- setup_frame(sa, regs, signr, oldmask);
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ setup_rt_frame(sig, ka, info, oldset, regs);
+ else
+ setup_frame(sig, ka, oldset, regs);
- if (sa->sa_flags & SA_ONESHOT)
- sa->sa_handler = NULL;
- if (!(sa->sa_flags & SA_NOMASK))
- current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
+ if (ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = NULL;
+
+ if (!(ka->sa.sa_flags & SA_NODEFER)) {
+ sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
+ sigaddset(¤t->blocked,sig);
+ recalc_sigpending(current);
+ }
}
/*
@@ -407,29 +927,28 @@
* that the kernel can handle, and then we build all the user-level signal
* handling stack-frames in one go after that.
*/
-asmlinkage int do_signal(unsigned long oldmask, struct pt_regs *regs)
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs)
{
- unsigned long mask = ~current->blocked;
- unsigned long signr;
- struct sigaction * sa;
+ siginfo_t info;
+ struct k_sigaction *ka;
current->tss.esp0 = (unsigned long) regs;
- /* If the process is traced, all signals are passed to the debugger. */
- if (current->flags & PF_PTRACED)
- mask = ~0UL;
- while ((signr = current->signal & mask)) {
- __asm__("bfffo %2,#0,#0,%1\n\t"
- "bfclr %0,%1,#1\n\t"
- "eorw #31,%1"
- :"=m" (current->signal),"=d" (signr)
- :"0" (current->signal), "1" (signr));
- sa = current->sig->action + signr;
- signr++;
+ if (!oldset)
+ oldset = ¤t->blocked;
+
+ for (;;) {
+ int signr;
+
+ signr = dequeue_signal(¤t->blocked, &info);
+
+ if (!signr)
+ break;
if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
current->exit_code = signr;
current->state = TASK_STOPPED;
+ regs->sr &= ~PS_T;
/* Did we come from a system call? */
if (regs->orig_d0 >= 0) {
@@ -443,11 +962,12 @@
}
notify_parent(current, SIGCHLD);
schedule();
+
+ /* We're back. Did the debugger cancel the sig? */
if (!(signr = current->exit_code)) {
discard_frame:
- /* Make sure that a faulted bus cycle
- isn't restarted (only needed on the
- 68030). */
+ /* Make sure that a faulted bus cycle isn't
+ restarted (only needed on the 680[23]0). */
if (regs->format == 10 || regs->format == 11) {
regs->stkadj = frame_extra_sizes[regs->format];
regs->format = 0;
@@ -455,26 +975,43 @@
continue;
}
current->exit_code = 0;
+
+ /* The debugger continued. Ignore SIGSTOP. */
if (signr == SIGSTOP)
goto discard_frame;
- if (_S(signr) & current->blocked) {
- current->signal |= _S(signr);
- mask &= ~_S(signr);
+
+ /* Update the siginfo structure. Is this good? */
+ if (signr != info.si_signo) {
+ info.si_signo = signr;
+ info.si_errno = 0;
+ info.si_code = SI_USER;
+ info.si_pid = current->p_pptr->pid;
+ info.si_uid = current->p_pptr->uid;
+ }
+
+ /* If the (new) signal is now blocked, requeue it. */
+ if (sigismember(¤t->blocked, signr)) {
+ send_sig_info(signr, &info, current);
continue;
}
- sa = current->sig->action + signr - 1;
}
- if (sa->sa_handler == SIG_IGN) {
+
+ ka = ¤t->sig->action[signr-1];
+ if (ka->sa.sa_handler == SIG_IGN) {
if (signr != SIGCHLD)
continue;
- /* check for SIGCHLD: it's special */
- while (sys_wait4(-1,NULL,WNOHANG, NULL) > 0)
+ /* Check for SIGCHLD: it's special. */
+ while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
/* nothing */;
continue;
}
- if (sa->sa_handler == SIG_DFL) {
+
+ if (ka->sa.sa_handler == SIG_DFL) {
+ int exit_code = signr;
+
if (current->pid == 1)
continue;
+
switch (signr) {
case SIGCONT: case SIGCHLD: case SIGWINCH:
continue;
@@ -482,31 +1019,35 @@
case SIGTSTP: case SIGTTIN: case SIGTTOU:
if (is_orphaned_pgrp(current->pgrp))
continue;
+ /* FALLTHRU */
+
case SIGSTOP:
- if (current->flags & PF_PTRACED)
- continue;
current->state = TASK_STOPPED;
current->exit_code = signr;
- if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags &
- SA_NOCLDSTOP))
+ if (!(current->p_pptr->sig->action[SIGCHLD-1]
+ .sa.sa_flags & SA_NOCLDSTOP))
notify_parent(current, SIGCHLD);
schedule();
continue;
case SIGQUIT: case SIGILL: case SIGTRAP:
case SIGIOT: case SIGFPE: case SIGSEGV:
- if (current->binfmt && current->binfmt->core_dump) {
- if (current->binfmt->core_dump(signr, regs))
- signr |= 0x80;
- }
- /* fall through */
+ if (current->binfmt
+ && current->binfmt->core_dump
+ && current->binfmt->core_dump(signr, regs))
+ exit_code |= 0x80;
+ /* FALLTHRU */
+
default:
- current->signal |= _S(signr & 0x7f);
+ sigaddset(¤t->signal, signr);
current->flags |= PF_SIGNALED;
- do_exit(signr);
+ do_exit(exit_code);
+ /* NOTREACHED */
}
}
- handle_signal(signr, sa, oldmask, regs);
+
+ /* Whee! Actually deliver the signal. */
+ handle_signal(signr, ka, &info, oldset, regs);
return 1;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov