patch-1.3.94 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: 558
- Date:
Thu Apr 18 02:07:17 1996
- Orig file:
v1.3.93/linux/arch/m68k/kernel/signal.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.93/linux/arch/m68k/kernel/signal.c linux/arch/m68k/kernel/signal.c
@@ -0,0 +1,557 @@
+/*
+ * 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 README.legal in the main directory of this archive
+ * for more details.
+ */
+
+/*
+ * 680x0 support by Hamish Macdonald
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+
+#include <asm/segment.h>
+#include <asm/pgtable.h>
+#include <asm/traps.h>
+#include <asm/bootinfo.h>
+
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+
+#define _S(nr) (1<<((nr)-1))
+
+#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+
+asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
+asmlinkage int do_signal(unsigned long oldmask, struct pt_regs *regs);
+
+static const int extra_sizes[16] = {
+ 0,
+ -1, /* sizeof(((struct frame *)0)->un.fmt1), */
+ sizeof(((struct frame *)0)->un.fmt2),
+ sizeof(((struct frame *)0)->un.fmt3),
+ sizeof(((struct frame *)0)->un.fmt4),
+ -1, /* sizeof(((struct frame *)0)->un.fmt5), */
+ -1, /* sizeof(((struct frame *)0)->un.fmt6), */
+ sizeof(((struct frame *)0)->un.fmt7),
+ -1, /* sizeof(((struct frame *)0)->un.fmt8), */
+ sizeof(((struct frame *)0)->un.fmt9),
+ sizeof(((struct frame *)0)->un.fmta),
+ sizeof(((struct frame *)0)->un.fmtb),
+ -1, /* sizeof(((struct frame *)0)->un.fmtc), */
+ -1, /* sizeof(((struct frame *)0)->un.fmtd), */
+ -1, /* sizeof(((struct frame *)0)->un.fmte), */
+ -1, /* sizeof(((struct frame *)0)->un.fmtf), */
+};
+
+/*
+ * 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;
+
+ current->blocked = newmask & _BLOCKABLE;
+ regs->d0 = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(oldmask, regs))
+ return -EINTR;
+ }
+}
+
+static unsigned char fpu_version = 0; /* version number of fpu, set by setup_frame */
+
+/*
+ * This sets regs->usp even though we don't actually use sigstacks yet..
+ */
+asmlinkage int do_sigreturn(unsigned long __unused)
+{
+ struct sigcontext_struct context;
+ struct frame * regs;
+ struct switch_stack *sw;
+ int fsize = 0;
+ int formatvec = 0;
+ unsigned long fp;
+ unsigned long usp = rdusp();
+
+#if 0
+ printk("sys_sigreturn, usp=%08x\n", (unsigned) usp);
+#endif
+
+ /* get stack frame pointer */
+ sw = (struct switch_stack *) &__unused;
+ regs = (struct frame *) (sw + 1);
+
+ /* get previous context (including pointer to possible extra junk) */
+ if (verify_area(VERIFY_READ, (void *)usp, sizeof(context)))
+ goto badframe;
+
+ memcpy_fromfs(&context,(void *)usp, sizeof(context));
+
+ fp = usp + sizeof (context);
+
+ /* restore signal mask */
+ current->blocked = context.sc_mask & _BLOCKABLE;
+
+ /* restore passed registers */
+ regs->ptregs.d0 = context.sc_d0;
+ regs->ptregs.d1 = context.sc_d1;
+ regs->ptregs.a0 = context.sc_a0;
+ regs->ptregs.a1 = context.sc_a1;
+ regs->ptregs.sr = (regs->ptregs.sr & 0xff00)|(context.sc_sr & 0xff);
+ regs->ptregs.pc = context.sc_pc;
+
+ wrusp(context.sc_usp);
+ formatvec = context.sc_formatvec;
+ regs->ptregs.format = formatvec >> 12;
+ regs->ptregs.vector = formatvec & 0xfff;
+ if (context.sc_fpstate[0])
+ {
+ /* Verify the frame format. */
+ if (context.sc_fpstate[0] != fpu_version){
+#if DEBUG
+ printk("fpregs=%08x fpcntl=%08x\n", context.sc_fpregs,
+ context.sc_fpcntl);
+ printk("Wrong fpu: sc_fpstate[0]=%02x fpu_version=%02x\n",
+ (unsigned) context.sc_fpstate[0], (unsigned) fpu_version);
+ {
+ int i;
+ printk("Saved fp_state: ");
+ for (i = 0; i < 216; i++){
+ printk("%02x ", context.sc_fpstate[i]);
+ }
+ printk("\n");
+ }
+#endif
+ goto badframe;
+ }
+ if (boot_info.cputype & FPU_68881)
+ {
+ if (context.sc_fpstate[1] != 0x18
+ && context.sc_fpstate[1] != 0xb4)
+ goto badframe;
+ }
+ else if (boot_info.cputype & FPU_68882)
+ {
+ if (context.sc_fpstate[1] != 0x38
+ && context.sc_fpstate[1] != 0xd4){
+#if 0
+ printk("Wrong 68882 fpu-state\n");
+#endif
+ goto badframe;
+ }
+ }
+ else if (boot_info.cputype & FPU_68040)
+ {
+ if (!((context.sc_fpstate[1] == 0x00)|| \
+ (context.sc_fpstate[1] == 0x28)|| \
+ (context.sc_fpstate[1] == 0x60))){
+#if 0
+ printk("Wrong 68040 fpu-state\n");
+#endif
+ goto badframe;
+ }
+ }
+ else if (boot_info.cputype & FPU_68060)
+ {
+ if (!((context.sc_fpstate[1] == 0x00)|| \
+ (context.sc_fpstate[1] == 0x60)|| \
+ (context.sc_fpstate[1] == 0xe0))){
+#if 0
+ printk("Wrong 68060 fpu-state\n");
+#endif
+ 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));
+
+ fsize = extra_sizes[regs->ptregs.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) {
+ if (verify_area(VERIFY_READ, (void *)fp, fsize))
+ goto badframe;
+
+#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 stuff */
+ "lsrl #2,%1\n\t"
+ "subql #1,%1\n\t"
+ "2: movesl %4@+,%2\n\t"
+ " movel %2,%/a0@+\n\t"
+ " dbra %1,2b\n\t"
+ "bral " SYMBOL_NAME_STR(ret_from_signal)
+ : /* no outputs, it doesn't ever return */
+ : "a" (sw), "d" (fsize), "d" (frame_offset/4-1),
+ "n" (frame_offset), "a" (fp)
+ : "a0");
+#undef frame_offset
+ goto badframe;
+ /* NOTREACHED */
+ }
+
+ return regs->ptregs.d0;
+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".
+ */
+
+#define UFRAME_SIZE(fs) (sizeof(struct sigcontext_struct)/4 + 6 + fs/4)
+
+static void setup_frame (struct sigaction * sa, unsigned long **fp,
+ unsigned long pc, struct frame *regs, int
+ signr, unsigned long oldmask)
+{
+ struct sigcontext_struct context;
+ unsigned long *frame, *tframe;
+ int fsize = extra_sizes[regs->ptregs.format];
+
+ if (fsize < 0) {
+ printk ("setup_frame: Unknown frame format %#x\n",
+ regs->ptregs.format);
+ do_exit(SIGSEGV);
+ }
+ frame = *fp - UFRAME_SIZE(fsize);
+ if (verify_area(VERIFY_WRITE,frame,UFRAME_SIZE(fsize)*4))
+ do_exit(SIGSEGV);
+ if (fsize) {
+ memcpy_tofs (frame + UFRAME_SIZE(0), ®s->un, fsize);
+ regs->ptregs.stkadj = fsize;
+ }
+
+/* set up the "normal" stack seen by the signal handler */
+ tframe = frame;
+
+ /* return address points to code on stack */
+ put_user((ulong)(frame+4), tframe); 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++;
+
+ put_user(regs->ptregs.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 = (unsigned long)*fp;
+ context.sc_d0 = regs->ptregs.d0;
+ context.sc_d1 = regs->ptregs.d1;
+ context.sc_a0 = regs->ptregs.a0;
+ context.sc_a1 = regs->ptregs.a1;
+ context.sc_sr = regs->ptregs.sr;
+ context.sc_pc = pc;
+ context.sc_formatvec = (regs->ptregs.format << 12 |
+ regs->ptregs.vector);
+#if DEBUG
+ printk("formatvec: %02x\n", (unsigned) context.sc_formatvec);
+#endif
+ __asm__ volatile ("fsave %0" : : "m" (*context.sc_fpstate) : "memory");
+ if (context.sc_fpstate[0])
+ {
+ fpu_version = context.sc_fpstate[0];
+#if DEBUG
+ {
+ int i;
+ printk("Saved fp_state: ");
+ for (i = 0; i < 216; i++){
+ printk("%02x ", context.sc_fpstate[i]);
+ }
+ printk("\n");
+ }
+ printk("fpregs=%08x fpcntl=%08x\n", context.sc_fpregs,
+ context.sc_fpcntl);
+#endif
+ __asm__ volatile ("fmovemx %/fp0-%/fp1,%0\n\t"
+ "fmoveml %/fpcr/%/fpsr/%/fpiar,%1"
+ : /* no outputs */
+ : "m" (*context.sc_fpregs),
+ "m" (*context.sc_fpcntl)
+ : "memory");
+ }
+#if DEBUG
+ {
+ int i;
+ printk("Saved fp_state: ");
+ for (i = 0; i < 216; i++){
+ printk("%02x ", context.sc_fpstate[i]);
+ }
+ printk("\n");
+ }
+#endif
+ memcpy_tofs (tframe, &context, sizeof(context));
+ /*
+ * no matter what frame format we were using before, we
+ * will do the "RTE" using a normal 4 word frame.
+ */
+ regs->ptregs.format = 0;
+
+ /* "return" new usp to caller */
+ *fp = frame;
+}
+
+/*
+ * 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.
+ *
+ * Note that we go through the signals twice: once to check the signals
+ * 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_in)
+{
+ unsigned long mask = ~current->blocked;
+ unsigned long handler_signal = 0;
+ unsigned long *frame = NULL;
+ unsigned long pc = 0;
+ unsigned long signr;
+ struct frame *regs = (struct frame *)regs_in;
+ struct sigaction * sa;
+
+ current->tss.esp0 = (unsigned long) regs;
+
+ 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),"=r" (signr)
+ :"1" (signr));
+ sa = current->sig->action + signr;
+ signr++;
+
+ if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
+ current->exit_code = signr;
+ current->state = TASK_STOPPED;
+ notify_parent(current);
+ schedule();
+ if (!(signr = current->exit_code)) {
+ discard_frame:
+ /* Make sure that a faulted bus cycle
+ isn't restarted. */
+ switch (regs->ptregs.format) {
+ case 7:
+ case 9:
+ case 10:
+ case 11:
+ regs->ptregs.stkadj = extra_sizes[regs->ptregs.format];
+ regs->ptregs.format = 0;
+ break;
+ }
+ continue;
+ }
+ current->exit_code = 0;
+ if (signr == SIGSTOP)
+ goto discard_frame;
+ if (_S(signr) & current->blocked) {
+ current->signal |= _S(signr);
+ continue;
+ }
+ sa = current->sig->action + signr - 1;
+ }
+ if (sa->sa_handler == SIG_IGN) {
+ if (signr != SIGCHLD)
+ continue;
+ /* check for SIGCHLD: it's special */
+ while (sys_waitpid(-1,NULL,WNOHANG) > 0)
+ /* nothing */;
+ continue;
+ }
+ if (sa->sa_handler == SIG_DFL) {
+ if (current->pid == 1)
+ continue;
+ switch (signr) {
+ case SIGCONT: case SIGCHLD: case SIGWINCH:
+ continue;
+
+ case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
+ 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))
+ notify_parent(current);
+ 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, (struct pt_regs *)regs))
+ signr |= 0x80;
+ }
+ /* fall through */
+ default:
+ current->signal |= _S(signr & 0x7f);
+ do_exit(signr);
+ }
+ }
+ /*
+ * OK, we're invoking a handler
+ */
+ if (regs->ptregs.orig_d0 >= 0) {
+ if (regs->ptregs.d0 == -ERESTARTNOHAND ||
+ (regs->ptregs.d0 == -ERESTARTSYS &&
+ !(sa->sa_flags & SA_RESTART)))
+ regs->ptregs.d0 = -EINTR;
+ }
+ handler_signal |= 1 << (signr-1);
+ mask &= ~sa->sa_mask;
+ }
+ if (regs->ptregs.orig_d0 >= 0 &&
+ (regs->ptregs.d0 == -ERESTARTNOHAND ||
+ regs->ptregs.d0 == -ERESTARTSYS ||
+ regs->ptregs.d0 == -ERESTARTNOINTR)) {
+ regs->ptregs.d0 = regs->ptregs.orig_d0;
+ regs->ptregs.pc -= 2;
+ }
+ if (!handler_signal) /* no handler will be called - return 0 */
+ {
+ /* If we are about to discard some frame stuff we must
+ copy over the remaining frame. */
+ if (regs->ptregs.stkadj)
+ {
+ struct frame *tregs =
+ (struct frame *) ((ulong) regs + regs->ptregs.stkadj);
+
+ /* This must be copied with decreasing addresses to
+ handle overlaps. */
+ tregs->ptregs.vector = regs->ptregs.vector;
+ tregs->ptregs.format = regs->ptregs.format;
+ tregs->ptregs.pc = regs->ptregs.pc;
+ tregs->ptregs.sr = regs->ptregs.sr;
+ }
+ return 0;
+ }
+ pc = regs->ptregs.pc;
+ frame = (unsigned long *)rdusp();
+ signr = 1;
+ sa = current->sig->action;
+ for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
+ if (mask > handler_signal)
+ break;
+ if (!(mask & handler_signal))
+ continue;
+ setup_frame(sa,&frame,pc,regs,signr,oldmask);
+ pc = (unsigned long) sa->sa_handler;
+ if (sa->sa_flags & SA_ONESHOT)
+ sa->sa_handler = NULL;
+/* force a supervisor-mode page-in of the signal handler to reduce races */
+ __asm__ __volatile__("movesb %0,%/d0": :"m" (*(char *)pc):"d0");
+ current->blocked |= sa->sa_mask;
+ oldmask |= sa->sa_mask;
+ }
+ wrusp((unsigned long)frame);
+ regs->ptregs.pc = pc;
+
+ /*
+ * if setup_frame saved some extra frame junk, we need to
+ * skip over that stuff when doing the RTE. This means we have
+ * to move the machine portion of the stack frame to where the
+ * "RTE" instruction expects it. The signal that we need to
+ * do this is that regs->stkadj is nonzero.
+ */
+ if (regs->ptregs.stkadj) {
+ struct frame *tregs =
+ (struct frame *)((ulong)regs + regs->ptregs.stkadj);
+#if DEBUG
+ printk("Performing stackadjust=%04x\n", (unsigned)
+ regs->ptregs.stkadj);
+#endif
+ /* This must be copied with decreasing addresses to
+ handle overlaps. */
+ tregs->ptregs.vector = regs->ptregs.vector;
+ tregs->ptregs.format = regs->ptregs.format;
+ tregs->ptregs.pc = regs->ptregs.pc;
+ tregs->ptregs.sr = regs->ptregs.sr;
+ }
+
+ return 1;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this