patch-2.1.44 linux/arch/mips/kernel/signal.c
Next file: linux/arch/mips/kernel/syscall.c
Previous file: linux/arch/mips/kernel/setup.c
Back to the patch index
Back to the overall index
- Lines: 532
- Date:
Mon Jul 7 08:18:53 1997
- Orig file:
v2.1.43/linux/arch/mips/kernel/signal.c
- Orig date:
Sun Jan 26 02:07:05 1997
diff -u --recursive --new-file v2.1.43/linux/arch/mips/kernel/signal.c linux/arch/mips/kernel/signal.c
@@ -2,7 +2,11 @@
* linux/arch/mips/kernel/signal.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1994, 1995, 1996 Ralf Baechle
+ *
+ * $Id: signal.c,v 1.7 1997/06/25 19:25:08 ralf Exp $
*/
+#include <linux/config.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
@@ -14,109 +18,110 @@
#include <linux/ptrace.h>
#include <linux/unistd.h>
+#include <asm/asm.h>
#include <asm/bitops.h>
-#include <asm/segment.h>
-#include <asm/cachectl.h>
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
#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 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);
+extern asmlinkage void (*save_fp_context)(struct sigcontext *sc);
+extern asmlinkage void (*restore_fp_context)(struct sigcontext *sc);
/*
- * atomically swap in the new signal mask, and wait for a signal.
+ * Atomically swap in the new signal mask, and wait for a signal.
+ * Unlike on Intel we pass a sigset_t *, not sigset_t.
*/
-asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set)
+asmlinkage int sys_sigsuspend(struct pt_regs *regs)
{
unsigned long mask;
- struct pt_regs * regs;
- int ret = -EINTR;
+ sigset_t *uset, set;
- lock_kernel();
- regs = (struct pt_regs *) &restart;
+ uset = (sigset_t *)(long) regs->regs[4];
+ if (get_user(set, uset))
+ return -EFAULT;
+
+ spin_lock_irq(¤t->sigmask_lock);
mask = current->blocked;
current->blocked = set & _BLOCKABLE;
- regs->reg2 = -EINTR;
+ spin_unlock_irq(¤t->sigmask_lock);
+
+ regs->regs[2] = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
- if (do_signal(mask,regs))
- goto out;
+ if (do_signal(mask, regs))
+ return -EINTR;
}
-out:
- unlock_kernel();
- return ret;
+ return -EINTR;
}
-/*
- * This sets regs->reg29 even though we don't actually use sigstacks yet..
- */
asmlinkage int sys_sigreturn(struct pt_regs *regs)
{
- struct sigcontext_struct *context;
- int ret;
+ struct sigcontext *context;
+ int i;
- lock_kernel();
- /*
- * We don't support fixing ADEL/ADES exceptions for signal stack frames.
- * No big loss - who doesn't care about the alignment of this stack
- * really deserves to loose.
- */
- context = (struct sigcontext_struct *) regs->reg29;
- if (verify_area(VERIFY_READ, context, sizeof(struct sigcontext_struct)) ||
- (regs->reg29 & 3))
+ context = (struct sigcontext *)(long) regs->regs[29];
+ if (!access_ok(VERIFY_READ, context, sizeof(struct sigcontext)) ||
+ (regs->regs[29] & (SZREG - 1)))
goto badframe;
- current->blocked = context->sc_oldmask & _BLOCKABLE;
- regs->reg1 = context->sc_at;
- regs->reg2 = context->sc_v0;
- regs->reg3 = context->sc_v1;
- regs->reg4 = context->sc_a0;
- regs->reg5 = context->sc_a1;
- regs->reg6 = context->sc_a2;
- regs->reg7 = context->sc_a3;
- regs->reg8 = context->sc_t0;
- regs->reg9 = context->sc_t1;
- regs->reg10 = context->sc_t2;
- regs->reg11 = context->sc_t3;
- regs->reg12 = context->sc_t4;
- regs->reg13 = context->sc_t5;
- regs->reg14 = context->sc_t6;
- regs->reg15 = context->sc_t7;
- regs->reg16 = context->sc_s0;
- regs->reg17 = context->sc_s1;
- regs->reg18 = context->sc_s2;
- regs->reg19 = context->sc_s3;
- regs->reg20 = context->sc_s4;
- regs->reg21 = context->sc_s5;
- regs->reg22 = context->sc_s6;
- regs->reg23 = context->sc_s7;
- regs->reg24 = context->sc_t8;
- regs->reg25 = context->sc_t9;
+ current->blocked = context->sc_sigset & _BLOCKABLE; /* XXX */
+ regs->cp0_epc = context->sc_pc; /* XXX */
+
+/*
+ * Disabled because we only use the lower 32 bit of the registers.
+ */
+#if 0
/*
- * Skip k0/k1
+ * We only allow user processes in 64bit mode (n32, 64 bit ABI) to
+ * restore the upper half of registers.
*/
- regs->reg28 = context->sc_gp;
- regs->reg29 = context->sc_sp;
- regs->reg30 = context->sc_fp;
- regs->reg31 = context->sc_ra;
- regs->cp0_epc = context->sc_epc;
- regs->cp0_cause = context->sc_cause;
+ if (read_32bit_cp0_register(CP0_STATUS) & ST0_UX) {
+ for(i = 31;i >= 0;i--)
+ __get_user(regs->regs[i], &context->sc_regs[i]);
+ __get_user(regs->hi, &context->sc_mdhi);
+ __get_user(regs->lo, &context->sc_mdlo);
+ } else
+#endif
+ {
+ long long reg;
+ for(i = 31;i >= 0;i--) {
+ __get_user(reg, &context->sc_regs[i]);
+ regs->regs[i] = (int) reg;
+ }
+ __get_user(reg, &context->sc_mdhi);
+ regs->hi = (int) reg;
+ __get_user(reg, &context->sc_mdlo);
+ regs->lo = (int) reg;
+ }
+
+ restore_fp_context(context);
/*
- * disable syscall checks
+ * Disable syscall checks
*/
regs->orig_reg2 = -1;
- goto out;
+
+ /*
+ * Don't let your children do this ...
+ */
+ __asm__ __volatile__(
+ "move\t$29,%0\n\t"
+ "j\tret_from_sys_call"
+ :/* no outputs */
+ :"r" (regs));
+ /* Unreached */
badframe:
+ lock_kernel();
do_exit(SIGSEGV);
-out:
- ret = context->sc_v0;
unlock_kernel();
- return ret;
}
/*
@@ -137,14 +142,14 @@
* one parameters in a0 (signum).
*
* usp -> [unused] ; first free word on stack
- * arg save space ; 16 bytes argument save space
- * code1 (addiu sp,#1-offset) ; syscall number
+ * arg save space ; 16/32 bytes arg. save space
+ * code1 (addiu sp,#1-offset) ; pop stackframe
* code2 (li v0,__NR_sigreturn) ; syscall number
* code3 (syscall) ; do sigreturn(2)
- * #1| at, v0, v1, a0, a1, a2, a3 ; All integer registers
- * | t0, t1, t2, t3, t4, t5, t6, t7 ; except zero, k0 and k1
+ * #1| $0, at, v0, v1, a0, a1, a2, a3 ; All integer registers
+ * | t0, t1, t2, t3, t4, t5, t6, t7 ; $0, k0 and k1 are placeholders
* | s0, s1, s2, s3, s4, s5, s6, s7
- * | t8, t9, gp, sp, fp, ra;
+ * | k0, k1, t8, t9, gp, sp, fp, ra;
* | epc ; old program counter
* | cause ; CP0 cause register
* | oldmask
@@ -153,27 +158,25 @@
struct sc {
unsigned long ass[4];
unsigned int code[4];
- struct sigcontext_struct scc;
+ struct sigcontext scc;
};
#define scc_offset ((size_t)&((struct sc *)0)->scc)
-static void setup_frame(struct sigaction * sa, struct sc **fp,
- unsigned long pc, struct pt_regs *regs,
+static void setup_frame(struct sigaction * sa, struct pt_regs *regs,
int signr, unsigned long oldmask)
{
struct sc *frame;
+ struct sigcontext *sc;
+ int i;
- frame = *fp;
+ /* Align the stackframe to an adequate boundary for the architecture. */
+ frame = (struct sc *) (long) regs->regs[29];
frame--;
+ frame = (struct sc *)((unsigned long)frame & ALMASK);
- /*
- * We don't support fixing ADEL/ADES exceptions for signal stack frames.
- * No big loss - who doesn't care about the alignment of this stack
- * really deserves to loose.
- */
- if (verify_area(VERIFY_WRITE, frame, sizeof (struct sc)) ||
- ((unsigned long)frame & 3))
- do_exit(SIGSEGV);
+ if (verify_area(VERIFY_WRITE, frame, sizeof (*frame)))
+ goto segv_and_exit;
+ sc = &frame->scc;
/*
* Set up the return code ...
@@ -184,64 +187,83 @@
* syscall
* .set reorder
*/
- frame->code[0] = 0x27bd0000 + scc_offset;
- frame->code[1] = 0x24020000 + __NR_sigreturn;
- frame->code[2] = 0x0000000c;
+ __put_user(0x27bd0000 + scc_offset, &frame->code[0]);
+ __put_user(0x24020000 + __NR_sigreturn, &frame->code[1]);
+ __put_user(0x0000000c, &frame->code[2]);
/*
* Flush caches so that the instructions will be correctly executed.
*/
- sys_cacheflush (frame->code, sizeof (frame->code), ICACHE);
+ flush_cache_sigtramp((unsigned long) frame->code);
/*
- * Set up the "normal" sigcontext_struct
+ * Set up the "normal" sigcontext
*/
- frame->scc.sc_at = regs->reg1; /* Assembler temporary */
- frame->scc.sc_v0 = regs->reg2; /* Result registers */
- frame->scc.sc_v1 = regs->reg3;
- frame->scc.sc_a0 = regs->reg4; /* Argument registers */
- frame->scc.sc_a1 = regs->reg5;
- frame->scc.sc_a2 = regs->reg6;
- frame->scc.sc_a3 = regs->reg7;
-
- frame->scc.sc_t0 = regs->reg8; /* Caller saved */
- frame->scc.sc_t1 = regs->reg9;
- frame->scc.sc_t2 = regs->reg10;
- frame->scc.sc_t3 = regs->reg11;
- frame->scc.sc_t4 = regs->reg12;
- frame->scc.sc_t5 = regs->reg13;
- frame->scc.sc_t6 = regs->reg14;
- frame->scc.sc_t7 = regs->reg15;
-
- frame->scc.sc_s0 = regs->reg16; /* Callee saved */
- frame->scc.sc_s1 = regs->reg17;
- frame->scc.sc_s2 = regs->reg18;
- frame->scc.sc_s3 = regs->reg19;
- frame->scc.sc_s4 = regs->reg20;
- frame->scc.sc_s5 = regs->reg21;
- frame->scc.sc_s6 = regs->reg22;
- frame->scc.sc_s7 = regs->reg23;
+ __put_user(regs->cp0_epc, &sc->sc_pc);
+ __put_user(regs->cp0_status, &sc->sc_status); /* Status register */
+ for(i = 31;i >= 0;i--)
+ __put_user(regs->regs[i], &sc->sc_regs[i]);
+ save_fp_context(sc);
+ __put_user(regs->hi, &sc->sc_mdhi);
+ __put_user(regs->lo, &sc->sc_mdlo);
+ __put_user(regs->cp0_cause, &sc->sc_cause);
+ __put_user((regs->cp0_status & ST0_CU1) != 0, &sc->sc_ownedfp);
+ __put_user(oldmask, &sc->sc_sigset);
+ __put_user(0, &sc->__pad0[0]);
+ __put_user(0, &sc->__pad0[1]);
+ __put_user(0, &sc->__pad0[2]);
+
+ regs->regs[4] = signr; /* Arguments for handler */
+ regs->regs[5] = 0; /* For now. */
+ regs->regs[6] = (long) frame; /* Pointer to sigcontext */
+ regs->regs[29] = (unsigned long) frame; /* Stack pointer */
+ regs->regs[31] = (unsigned long) frame->code; /* Return address */
+ regs->cp0_epc = (unsigned long) sa->sa_handler; /* "return" to the first handler */
+ regs->regs[25] = regs->cp0_epc; /* PIC shit... */
+ return;
- frame->scc.sc_t8 = regs->reg24; /* Caller saved */
- frame->scc.sc_t9 = regs->reg25;
-
- /*
- * Don't copy k0/k1
- */
- frame->scc.sc_gp = regs->reg28; /* global pointer / s8 */
- frame->scc.sc_sp = regs->reg29; /* old stack pointer */
- frame->scc.sc_fp = regs->reg30; /* old frame pointer */
- frame->scc.sc_ra = regs->reg31; /* old return address */
+segv_and_exit:
+ lock_kernel();
+ do_exit(SIGSEGV);
+ unlock_kernel();
+}
- frame->scc.sc_epc = regs->cp0_epc; /* Program counter */
- frame->scc.sc_cause = regs->cp0_cause; /* c0_epc register */
+static inline void handle_signal(unsigned long signr, struct sigaction *sa,
+ unsigned long oldmask, struct pt_regs * regs)
+{
+ setup_frame(sa, regs, signr, oldmask);
- frame->scc.sc_oldmask = oldmask;
- *fp = frame;
+ if (sa->sa_flags & SA_ONESHOT)
+ sa->sa_handler = NULL;
+ if (!(sa->sa_flags & SA_NOMASK)) {
+ spin_lock_irq(¤t->sigmask_lock);
+ current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
+ spin_unlock_irq(¤t->sigmask_lock);
+ }
+}
- regs->reg4 = signr; /* argument for handler */
+static inline void syscall_restart(unsigned long r0, unsigned long or2,
+ unsigned long or7, struct pt_regs *regs,
+ struct sigaction *sa)
+{
+ switch(r0) {
+ case ERESTARTNOHAND:
+ no_system_call_restart:
+ regs->regs[0] = regs->regs[2] = EINTR;
+ break;
+ case ERESTARTSYS:
+ if(!(sa->sa_flags & SA_RESTART))
+ goto no_system_call_restart;
+ /* fallthrough */
+ case ERESTARTNOINTR:
+ regs->regs[0] = regs->regs[2] = or2;
+ regs->regs[7] = or7;
+ regs->cp0_epc -= 8;
+ }
}
+extern int do_irix_signal(unsigned long oldmask, struct pt_regs *regs);
+
/*
* 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
@@ -253,16 +275,15 @@
*/
asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
{
- unsigned long mask;
- unsigned long handler_signal = 0;
- struct sc *frame = NULL;
- unsigned long pc = 0;
- unsigned long signr;
+ unsigned long mask = ~current->blocked;
+ unsigned long signr, r0 = regs->regs[0];
+ unsigned long r7 = regs->orig_reg7;
struct sigaction * sa;
- int ret;
- lock_kernel();
- mask = ~current->blocked;
+#ifdef CONFIG_BINFMT_IRIX
+ if(current->personality != PER_LINUX)
+ return do_irix_signal(oldmask, regs);
+#endif
while ((signr = current->signal & mask)) {
signr = ffz(~signr);
clear_bit(signr, ¤t->signal);
@@ -279,7 +300,9 @@
if (signr == SIGSTOP)
continue;
if (_S(signr) & current->blocked) {
+ spin_lock_irq(¤t->sigmask_lock);
current->signal |= _S(signr);
+ spin_unlock_irq(¤t->sigmask_lock);
continue;
}
sa = current->sig->action + signr - 1;
@@ -288,7 +311,7 @@
if (signr != SIGCHLD)
continue;
/* check for SIGCHLD: it's special */
- while (sys_waitpid(-1,NULL,WNOHANG) > 0)
+ while (sys_wait4(-1,NULL,WNOHANG, NULL) > 0)
/* nothing */;
continue;
}
@@ -299,7 +322,10 @@
case SIGCONT: case SIGCHLD: case SIGWINCH:
continue;
- case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
+ case SIGTSTP: case SIGTTIN: case SIGTTOU:
+ if (is_orphaned_pgrp(current->pgrp))
+ continue;
+ case SIGSTOP:
if (current->flags & PF_PTRACED)
continue;
current->state = TASK_STOPPED;
@@ -311,68 +337,58 @@
continue;
case SIGQUIT: case SIGILL: case SIGTRAP:
- case SIGIOT: case SIGFPE: case SIGSEGV: case SIGBUS:
+ case SIGABRT: case SIGFPE: case SIGSEGV:
+ case SIGBUS:
+ lock_kernel();
if (current->binfmt && current->binfmt->core_dump) {
if (current->binfmt->core_dump(signr, regs))
signr |= 0x80;
}
+ unlock_kernel();
/* fall through */
default:
+ spin_lock_irq(¤t->sigmask_lock);
current->signal |= _S(signr & 0x7f);
+ spin_unlock_irq(¤t->sigmask_lock);
+
current->flags |= PF_SIGNALED;
+
+ lock_kernel(); /* 8-( */
do_exit(signr);
+ unlock_kernel();
}
}
/*
* OK, we're invoking a handler
*/
- if (regs->orig_reg2 >= 0) {
- if (regs->reg2 == -ERESTARTNOHAND ||
- (regs->reg2 == -ERESTARTSYS &&
- !(sa->sa_flags & SA_RESTART)))
- regs->reg2 = -EINTR;
- }
- handler_signal |= 1 << (signr-1);
- mask &= ~sa->sa_mask;
+ if(r0)
+ syscall_restart(r0, regs->orig_reg2,
+ r7, regs, sa);
+ handle_signal(signr, sa, oldmask, regs);
+ return 1;
}
/*
* Who's code doesn't conform to the restartable syscall convention
* dies here!!! The li instruction, a single machine instruction,
* must directly be followed by the syscall instruction.
*/
- if (regs->orig_reg2 >= 0 &&
- (regs->reg2 == -ERESTARTNOHAND ||
- regs->reg2 == -ERESTARTSYS ||
- regs->reg2 == -ERESTARTNOINTR))
- {
- regs->reg2 = regs->orig_reg2;
+ if (r0 &&
+ (regs->regs[2] == ERESTARTNOHAND ||
+ regs->regs[2] == ERESTARTSYS ||
+ regs->regs[2] == ERESTARTNOINTR)) {
+ regs->regs[0] = regs->regs[2] = regs->orig_reg2;
+ regs->regs[7] = r7;
regs->cp0_epc -= 8;
}
- ret = 0;
- if (!handler_signal) /* no handler will be called - return 0 */
- goto out;
- pc = regs->cp0_epc;
- frame = (struct sc *) regs->reg29;
- 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;
- current->blocked |= sa->sa_mask;
- oldmask |= sa->sa_mask;
- }
- regs->reg29 = (unsigned long) frame; /* Stack pointer */
- regs->reg31 = (unsigned long) frame->code; /* Return address */
- regs->cp0_epc = pc; /* "return" to the first handler */
+ return 0;
+}
- ret = 1;
-out:
- unlock_kernel();
- return ret;
+/*
+ * The signal(2) syscall is no longer available in the kernel
+ * because GNU libc doesn't use it. Maybe I'll add it back to the
+ * kernel for the binary compatibility stuff.
+ */
+asmlinkage unsigned long sys_signal(int signum, __sighandler_t handler)
+{
+ return -ENOSYS;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov