patch-2.1.68 linux/arch/i386/kernel/signal.c

Next file: linux/arch/i386/kernel/vm86.c
Previous file: linux/arch/i386/kernel/ptrace.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.67/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
@@ -2,6 +2,8 @@
  *  linux/arch/i386/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  1997-11-28  Modified for POSIX.1b signals by Richard Henderson
  */
 
 #include <linux/config.h>
@@ -16,40 +18,132 @@
 #include <linux/wait.h>
 #include <linux/ptrace.h>
 #include <linux/unistd.h>
+#include <linux/stddef.h>
 
 #include <asm/uaccess.h>
 
-#define _S(nr) (1<<((nr)-1))
+#define DEBUG_SIG 0
 
-#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);
 
 /*
- * 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 sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set)
+asmlinkage int
+sys_sigsuspend(int history0, int history1, old_sigset_t mask)
 {
-	struct pt_regs * regs = (struct pt_regs *) &restart;
-	unsigned long mask;
+	struct pt_regs * regs = (struct pt_regs *) &history0;
+	sigset_t saveset;
 
+	mask &= _BLOCKABLE;
 	spin_lock_irq(&current->sigmask_lock);
-	mask = current->blocked;
-	current->blocked = set & _BLOCKABLE;
+	saveset = current->blocked;
+	siginitset(&current->blocked, mask);
+	recalc_sigpending(current);
 	spin_unlock_irq(&current->sigmask_lock);
 
 	regs->eax = -EINTR;
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(mask, regs))
+		if (do_signal(&saveset, regs))
 			return -EINTR;
 	}
 }
 
+asmlinkage int
+sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize)
+{
+	struct pt_regs * regs = (struct pt_regs *) &unewset;
+	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);
+
+	spin_lock_irq(&current->sigmask_lock);
+	saveset = current->blocked;
+	current->blocked = newset;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+	regs->eax = -EINTR;
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		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;
+	struct sigcontext sc;
+	struct _fpstate fpstate;
+	unsigned long extramask[_NSIG_WORDS-1];
+	char retcode[8];
+};
+
+struct rt_sigframe
+{
+	char *pretcode;
+	int sig;
+	struct siginfo *pinfo;
+	void *puc;
+	struct siginfo info;
+	struct ucontext uc;
+	struct _fpstate fpstate;
+	char retcode[8];
+};
+
+
 static inline void restore_i387_hard(struct _fpstate *buf)
 {
 #ifdef __SMP__
@@ -64,94 +158,150 @@
 #endif
 	current->used_math = 1;
 	current->flags &= ~PF_USEDFPU;
-	copy_from_user(&current->tss.i387.hard, buf, sizeof(*buf));
+	__copy_from_user(&current->tss.i387.hard, buf, sizeof(*buf));
 }
 
-static void restore_i387(struct _fpstate *buf)
+static inline void restore_i387(struct _fpstate *buf)
 {
 #ifndef CONFIG_MATH_EMULATION
 	restore_i387_hard(buf);
 #else
-	if (hard_math) {
+	if (hard_math)
 		restore_i387_hard(buf);
-		return;
-	}
-	restore_i387_soft(buf);
-#endif	
+	else
+		restore_i387_soft(buf);
+#endif
 }
-	
 
-/*
- * This sets regs->esp even though we don't actually use sigstacks yet..
- */
-asmlinkage int sys_sigreturn(unsigned long __unused)
+static int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
 {
-#define COPY(x) regs->x = context->x
-#define COPY_SEG(seg) \
-{ unsigned int tmp = context->seg; \
-if (   (tmp & 0xfffc)     /* not a NULL selectors */ \
-    && (tmp & 0x4) != 0x4 /* not a LDT selector */ \
-    && (tmp & 3) != 3     /* not a RPL3 GDT selector */ \
-   ) goto badframe; \
-regs->x##seg = tmp; }
-#define COPY_SEG_STRICT(seg) \
-{ unsigned int tmp = context->seg; \
-if ((tmp & 0xfffc) && (tmp & 3) != 3) goto badframe; \
-regs->x##seg = tmp; }
-#define GET_SEG(seg) \
-{ unsigned int tmp = context->seg; \
-if (   (tmp & 0xfffc)     /* not a NULL selectors */ \
-    && (tmp & 0x4) != 0x4 /* not a LDT selector */ \
-    && (tmp & 3) != 3     /* not a RPL3 GDT selector */ \
-   ) goto badframe; \
-__asm__("mov %w0,%%" #seg: :"r" (tmp)); }
-	struct sigcontext * context;
-	struct pt_regs * regs;
-
-	regs = (struct pt_regs *) &__unused;
-	context = (struct sigcontext *) regs->esp;
-	if (verify_area(VERIFY_READ, context, sizeof(*context)))
-		goto badframe;
-	current->blocked = context->oldmask & _BLOCKABLE;
-	COPY_SEG(ds);
-	COPY_SEG(es);
-	GET_SEG(fs);
+	unsigned int tmp;
+
+#define COPY(x)		__get_user(regs->x, &sc->x)
+
+#define COPY_SEG(seg)							\
+	{ __get_user(tmp, &sc->seg);					\
+	  if ((tmp & 0xfffc)		/* not a NULL selectors */	\
+	      && (tmp & 0x4) != 0x4	/* not a LDT selector */	\
+	      && (tmp & 3) != 3)	/* not a RPL3 GDT selector */	\
+		  goto badframe;					\
+	  regs->x##seg = tmp; }
+
+#define COPY_SEG_STRICT(seg)						\
+	{ __get_user(tmp, &sc->seg);					\
+	  if ((tmp & 0xfffc) && (tmp & 3) != 3) goto badframe;		\
+	  regs->x##seg = tmp; }
+
+#define GET_SEG(seg)							\
+	{ __get_user(tmp, &sc->seg);					\
+	  if ((tmp & 0xfffc)		/* not a NULL selectors */	\
+	      && (tmp & 0x4) != 0x4	/* not a LDT selector */	\
+	      && (tmp & 3) != 3)	/* not a RPL3 GDT selector */	\
+		  goto badframe;					\
+	  __asm__ __volatile__("mov %w0,%%" #seg : : "r"(tmp)); }
+
 	GET_SEG(gs);
-	COPY_SEG_STRICT(ss);
-	COPY_SEG_STRICT(cs);
-	COPY(eip);
-	COPY(ecx); COPY(edx);
+	GET_SEG(fs);
+	COPY_SEG(es);
+	COPY_SEG(ds);
+	COPY(edi);
+	COPY(esi);
+	COPY(ebp);
+	COPY(esp);
 	COPY(ebx);
-	COPY(esp); COPY(ebp);
-	COPY(edi); COPY(esi);
-	regs->eflags &= ~0x40DD5;
-	regs->eflags |= context->eflags & 0x40DD5;
+	COPY(edx);
+	COPY(ecx);
+	COPY(eip);
+	COPY_SEG_STRICT(cs);
+	COPY_SEG_STRICT(ss);
+	
+	__get_user(tmp, &sc->eflags);
+	regs->eflags = (regs->eflags & ~0x40DD5) | (tmp & 0x40DD5);
 	regs->orig_eax = -1;		/* disable syscall checks */
-	if (context->fpstate) {
-		struct _fpstate * buf = context->fpstate;
+
+	__get_user(tmp, (unsigned long *)&sc->fpstate);
+	if (tmp) {
+		struct _fpstate * buf = (struct _fpstate *) tmp;
 		if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
 			goto badframe;
 		restore_i387(buf);
 	}
-	return context->eax;
+
+	__get_user(tmp, &sc->eax);
+	return tmp;
 
 badframe:
 	lock_kernel();
 	do_exit(SIGSEGV);
-	unlock_kernel();
 }
 
+asmlinkage int sys_sigreturn(unsigned long __unused)
+{
+	struct pt_regs *regs = (struct pt_regs *) &__unused;
+	struct sigframe *frame = (struct sigframe *)(regs->esp - 8);
+	sigset_t set;
+
+	if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+		goto badframe;
+	if (__get_user(set.sig[0], &frame->sc.oldmask)
+	    || (_NSIG_WORDS > 1
+		&& __copy_from_user(&set.sig[1], &frame->extramask,
+				    sizeof(frame->extramask))))
+		goto badframe;
+
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	
+	return restore_sigcontext(regs, &frame->sc);
+
+badframe:
+	lock_kernel();
+	do_exit(SIGSEGV);
+}	
+
+asmlinkage int sys_rt_sigreturn(unsigned long __unused)
+{
+	struct pt_regs *regs = (struct pt_regs *) &__unused;
+	struct rt_sigframe *frame = (struct rt_sigframe *)(regs->esp - 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);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	
+	return restore_sigcontext(regs, &frame->uc.uc_mcontext);
+
+badframe:
+	lock_kernel();
+	do_exit(SIGSEGV);
+}	
+
+/*
+ * Set up a signal frame.
+ */
+
 static inline struct _fpstate * save_i387_hard(struct _fpstate * buf)
 {
 #ifdef __SMP__
 	if (current->flags & PF_USEDFPU) {
-		__asm__ __volatile__("fnsave %0":"=m" (current->tss.i387.hard));
+		__asm__ __volatile__("fnsave %0":"=m"(current->tss.i387.hard));
 		stts();
 		current->flags &= ~PF_USEDFPU;
 	}
 #else
 	if (current == last_task_used_math) {
-		__asm__ __volatile__("fnsave %0":"=m" (current->tss.i387.hard));
+		__asm__ __volatile__("fnsave %0":"=m"(current->tss.i387.hard));
 		last_task_used_math = NULL;
 		__asm__ __volatile__("fwait");	/* not needed on 486+ */
 		stts();
@@ -163,7 +313,7 @@
 	return buf;
 }
 
-static struct _fpstate * save_i387(struct _fpstate * buf)
+static struct _fpstate * save_i387(struct _fpstate *buf)
 {
 	if (!current->used_math)
 		return NULL;
@@ -171,85 +321,168 @@
 #ifndef CONFIG_MATH_EMULATION
 	return save_i387_hard(buf);
 #else
-	if (hard_math)
-		return save_i387_hard(buf);
-	return save_i387_soft(buf);
+	return hard_math ? save_i387_hard(buf) : save_i387_soft(buf);
 #endif
 }
 
-/*
- * Set up a signal frame... Make the stack look the way iBCS2 expects
- * it to look.
- */
-static void setup_frame(struct sigaction * sa,
-	struct pt_regs * regs, int signr,
-	unsigned long oldmask)
-{
-	unsigned long * frame;
-
-	frame = (unsigned long *) regs->esp;
-	if ((regs->xss & 0xffff) != USER_DS && sa->sa_restorer)
-		frame = (unsigned long *) sa->sa_restorer;
-	frame -= 64;
-	if (!access_ok(VERIFY_WRITE,frame,64*4))
-		goto segv_and_exit;
+static void
+setup_sigcontext(struct sigcontext *sc, struct _fpstate *fpstate,
+		 struct pt_regs *regs, unsigned long mask)
+{
+	unsigned int tmp;
 
-/* set up the "normal" stack seen by the signal handler (iBCS2) */
-#define __CODE ((unsigned long)(frame+24))
-#define CODE(x) ((unsigned long *) ((x)+__CODE))
-	
-    /* XXX Can possible miss a SIGSEGV when frame crosses a page border
-       and a thread unmaps it while we are accessing it. 
-       So either check all put_user() calls or don't do it at all.  
-       We use __put_user() here because the access_ok() call was already
-       done earlier. */  
-	if (__put_user(__CODE,frame))
+	tmp = 0;
+	__asm__("mov %%gs,%w0" : "=r"(tmp): "0"(tmp));
+	__put_user(tmp, (unsigned int *)&sc->gs);
+	__asm__("mov %%fs,%w0" : "=r"(tmp): "0"(tmp));
+	__put_user(tmp, (unsigned int *)&sc->fs);
+
+	__put_user(regs->xes, (unsigned int *)&sc->es);
+	__put_user(regs->xds, (unsigned int *)&sc->ds);
+	__put_user(regs->edi, &sc->edi);
+	__put_user(regs->esi, &sc->esi);
+	__put_user(regs->ebp, &sc->ebp);
+	__put_user(regs->esp, &sc->esp);
+	__put_user(regs->ebx, &sc->ebx);
+	__put_user(regs->edx, &sc->edx);
+	__put_user(regs->ecx, &sc->ecx);
+	__put_user(regs->eax, &sc->eax);
+	__put_user(current->tss.trap_no, &sc->trapno);
+	__put_user(current->tss.error_code, &sc->err);
+	__put_user(regs->eip, &sc->eip);
+	__put_user(regs->xcs, (unsigned int *)&sc->cs);
+	__put_user(regs->eflags, &sc->eflags);
+	__put_user(regs->esp, &sc->esp_at_signal);
+	__put_user(regs->xss, (unsigned int *)&sc->ss);
+
+	__put_user(save_i387(fpstate), &sc->fpstate);
+
+	/* non-iBCS2 extensions.. */
+	__put_user(mask, &sc->oldmask);
+	__put_user(current->tss.cr2, &sc->cr2);
+}	
+
+static void setup_frame(int sig, struct k_sigaction *ka,
+			sigset_t *set, struct pt_regs * regs)
+{
+	struct sigframe *frame;
+
+	frame = (struct sigframe *)((regs->esp - sizeof(*frame)) & -8);
+
+	/* XXX: Check here if we need to switch stacks.. */
+
+	/* This is legacy signal stack switching.  */
+	if ((regs->xss & 0xffff) != USER_DS
+	    && !(ka->sa.sa_flags & SA_RESTORER) && ka->sa.sa_restorer)
+		frame = (struct sigframe *) ka->sa.sa_restorer;
+
+	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
 		goto segv_and_exit;
-	if (current->exec_domain && current->exec_domain->signal_invmap)
-		__put_user(current->exec_domain->signal_invmap[signr], frame+1);
-	else
-		__put_user(signr, frame+1);
+
+	__put_user((current->exec_domain
+		    && current->exec_domain->signal_invmap
+		    && sig < 32
+		    ? current->exec_domain->signal_invmap[sig]
+		    : sig),
+		   &frame->sig);
+
+	setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]);
+
+	if (_NSIG_WORDS > 1) {
+		__copy_to_user(frame->extramask, &set->sig[1],
+			       sizeof(frame->extramask));
+	}
+
+	/* Set up to return from userspace.  If provided, use a stub
+	   already in userspace.  */
+	if (ka->sa.sa_flags & SA_RESTORER) {
+		__put_user(ka->sa.sa_restorer, &frame->pretcode);
+	} else {
+		__put_user(frame->retcode, &frame->pretcode);
+		/* This is popl %eax ; movl $,%eax ; int $0x80 */
+		__put_user(0xb858, (short *)(frame->retcode+0));
+		__put_user(__NR_sigreturn, (int *)(frame->retcode+2));
+		__put_user(0x80cd, (short *)(frame->retcode+6));
+	}
+
+	/* Set up registers for signal handler */
+	regs->esp = (unsigned long) frame;
+	regs->eip = (unsigned long) ka->sa.sa_handler;
 	{
-		unsigned int tmp = 0;
-#define PUT_SEG(seg, mem) \
-__asm__("mov %%" #seg",%w0":"=r" (tmp):"0" (tmp)); __put_user(tmp,mem);
-		PUT_SEG(gs, frame+2);
-		PUT_SEG(fs, frame+3);
-	}
-	__put_user(regs->xes, frame+4);
-	__put_user(regs->xds, frame+5);
-	__put_user(regs->edi, frame+6);
-	__put_user(regs->esi, frame+7);
-	__put_user(regs->ebp, frame+8);
-	__put_user(regs->esp, frame+9);
-	__put_user(regs->ebx, frame+10);
-	__put_user(regs->edx, frame+11);
-	__put_user(regs->ecx, frame+12);
-	__put_user(regs->eax, frame+13);
-	__put_user(current->tss.trap_no, frame+14);
-	__put_user(current->tss.error_code, frame+15);
-	__put_user(regs->eip, frame+16);
-	__put_user(regs->xcs, frame+17);
-	__put_user(regs->eflags, frame+18);
-	__put_user(regs->esp, frame+19);
-	__put_user(regs->xss, frame+20);
-	__put_user((unsigned long) save_i387((struct _fpstate *)(frame+32)),frame+21);
-/* non-iBCS2 extensions.. */
-	__put_user(oldmask, frame+22);
-	__put_user(current->tss.cr2, frame+23);
-/* set up the return code... */
-	__put_user(0x0000b858, CODE(0));	/* popl %eax ; movl $,%eax */
-	__put_user(0x80cd0000, CODE(4));	/* int $0x80 */
-	__put_user(__NR_sigreturn, CODE(2));
-#undef __CODE
-#undef CODE
+		unsigned long seg = USER_DS;
+		__asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg));
+		set_fs(seg);
+		regs->xds = seg;
+		regs->xes = seg;
+		regs->xss = seg;
+		regs->xcs = USER_CS;
+	}
+	regs->eflags &= ~TF_MASK;
+
+#if DEBUG_SIG
+	printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
+		current->comm, current->pid, frame, regs->eip, frame->pretcode);
+#endif
+
+	return;
+
+segv_and_exit:
+	lock_kernel();
+	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;
+
+	frame = (struct rt_sigframe *)((regs->esp - sizeof(*frame)) & -8);
+
+	/* XXX: Check here if we need to switch stacks.. */
+
+	/* This is legacy signal stack switching.  */
+	if ((regs->xss & 0xffff) != USER_DS
+	    && !(ka->sa.sa_flags & SA_RESTORER) && ka->sa.sa_restorer)
+		frame = (struct rt_sigframe *) ka->sa.sa_restorer;
+
+	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+		goto segv_and_exit;
+
+	__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));
+
+	setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,
+			 regs, set->sig[0]);
+	__copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+	/* Set up to return from userspace.  If provided, use a stub
+	   already in userspace.  */
+	if (ka->sa.sa_flags & SA_RESTORER) {
+		__put_user(ka->sa.sa_restorer, &frame->pretcode);
+	} else {
+		__put_user(frame->retcode, &frame->pretcode);
+		/* This is movl $,%eax ; int $0x80 */
+		__put_user(0xb8, (char *)(frame->retcode+0));
+		__put_user(__NR_rt_sigreturn, (int *)(frame->retcode+1));
+		__put_user(0x80cd, (short *)(frame->retcode+5));
+	}
 
 	/* Set up registers for signal handler */
 	regs->esp = (unsigned long) frame;
-	regs->eip = (unsigned long) sa->sa_handler;
+	regs->eip = (unsigned long) ka->sa.sa_handler;
 	{
 		unsigned long seg = USER_DS;
-		__asm__("mov %w0,%%fs ; mov %w0,%%gs":"=r" (seg) :"0" (seg));
+		__asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg));
 		set_fs(seg);
 		regs->xds = seg;
 		regs->xes = seg;
@@ -257,21 +490,28 @@
 		regs->xcs = USER_CS;
 	}
 	regs->eflags &= ~TF_MASK;
+
+#if DEBUG_SIG
+	printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
+		current->comm, current->pid, frame, regs->eip, frame->pretcode);
+#endif
+
 	return;
 
 segv_and_exit:
 	lock_kernel();
 	do_exit(SIGSEGV);
-	unlock_kernel();
 }
 
 /*
  * OK, we're invoking a handler
  */	
-static void handle_signal(unsigned long signr, struct sigaction *sa,
-	unsigned long oldmask, struct pt_regs * regs)
+
+static void
+handle_signal(unsigned long sig, struct k_sigaction *ka,
+	      siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
 {
-	/* are we from a system call? */
+	/* Are we from a system call? */
 	if (regs->orig_eax >= 0) {
 		/* If so, check system call restarting.. */
 		switch (regs->eax) {
@@ -280,7 +520,7 @@
 				break;
 
 			case -ERESTARTSYS:
-				if (!(sa->sa_flags & SA_RESTART)) {
+				if (!(ka->sa.sa_flags & SA_RESTART)) {
 					regs->eax = -EINTR;
 					break;
 				}
@@ -291,14 +531,20 @@
 		}
 	}
 
-	/* set up the stack frame */
-	setup_frame(sa, regs, signr, oldmask);
+	/* Set up the stack frame */
+	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)) {
+	if (ka->sa.sa_flags & SA_ONESHOT)
+		ka->sa.sa_handler = SIG_DFL;
+
+	if (!(ka->sa.sa_flags & SA_NODEFER)) {
 		spin_lock_irq(&current->sigmask_lock);
-		current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
+		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+		sigaddset(&current->blocked,sig);
+		recalc_sigpending(current);
 		spin_unlock_irq(&current->sigmask_lock);
 	}
 }
@@ -312,107 +558,115 @@
  * 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;
-	unsigned long signr;
-	struct sigaction * sa;
+	sigset_t _oldset;
+	siginfo_t info;
+	unsigned long signr, core = 0;
+	struct k_sigaction *ka;
 
 	/*
-	 *    We want the common case to go fast, which
+	 * We want the common case to go fast, which
 	 * is why we may in certain cases get here from
 	 * kernel mode. Just return without doing anything
 	 * if so.
 	 */
 	if ((regs->xcs & 3) != 3)
 		return 1;
-	mask = ~current->blocked;
-	while ((signr = current->signal & mask)) {
-		/*
-		 *	This stops gcc flipping out. Otherwise the assembler
-		 *	including volatiles for the inline function to get
-		 *	current combined with this gets it confused.
-		 */
-		struct task_struct *t=current;
-		__asm__("bsf %3,%1\n\t"
-#ifdef __SMP__
-			"lock ; "
-#endif
-			"btrl %1,%0"
-			:"=m" (t->signal),"=r" (signr)
-			:"0" (t->signal), "1" (signr));
-		sa = current->sig->action + signr;
-		signr++;
+
+	spin_lock_irq(&current->sigmask_lock);
+	if (!oldset) {
+		_oldset = current->blocked;
+		oldset = &_oldset;
+	}
+ 	while ((signr = dequeue_signal(&current->blocked, &info)) != 0) {
+		spin_unlock_irq(&current->sigmask_lock);
+
 		if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
+			/* Let the debugger run.  */
 			current->exit_code = signr;
 			current->state = TASK_STOPPED;
 			notify_parent(current, SIGCHLD);
 			schedule();
+
+			/* We're back.  Did the debugger cancel the sig?  */
 			if (!(signr = current->exit_code))
-				continue;
+				goto skip_signal;
 			current->exit_code = 0;
+
+			/* The debugger continued.  Ignore SIGSTOP.  */
 			if (signr == SIGSTOP)
-				continue;
-			if (_S(signr) & current->blocked) {
-				spin_lock_irq(&current->sigmask_lock);
-				current->signal |= _S(signr);
-				spin_unlock_irq(&current->sigmask_lock);
-				continue;
+				goto skip_signal;
+
+			/* 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(&current->blocked, signr)) {
+				send_sig_info(signr, &info, current);
+				goto skip_signal;
 			}
-			sa = current->sig->action + signr - 1;
-		}
-		if (sa->sa_handler == SIG_IGN) {
-			if (signr != SIGCHLD)
-				continue;
-			/* check for SIGCHLD: it's special */
-			while (sys_wait4(-1,NULL,WNOHANG, NULL) > 0)
-				/* nothing */;
-			continue;
 		}
-		if (sa->sa_handler == SIG_DFL) {
+
+		ka = &current->sig->action[signr-1];
+		if (ka->sa.sa_handler == SIG_DFL) {
+			/* Init gets no signals it doesn't want.  */
 			if (current->pid == 1)
-				continue;
+				goto skip_signal;
+
 			switch (signr) {
 			case SIGCONT: case SIGCHLD: case SIGWINCH:
-				continue;
+				goto skip_signal;
 
 			case SIGTSTP: case SIGTTIN: case SIGTTOU:
 				if (is_orphaned_pgrp(current->pgrp))
-					continue;
+					goto skip_signal;
+				/* 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;
+				break;
 
 			case SIGQUIT: case SIGILL: case SIGTRAP:
 			case SIGABRT: case SIGFPE: case SIGSEGV:
 				lock_kernel();
-				if (current->binfmt && current->binfmt->core_dump) {
-					if (current->binfmt->core_dump(signr, regs))
-						signr |= 0x80;
-				}
+				if (current->binfmt
+				    && current->binfmt->core_dump
+				    &&current->binfmt->core_dump(signr, regs))
+					core = 0x80;
 				unlock_kernel();
-				/* fall through */
-			default:
-				spin_lock_irq(&current->sigmask_lock);
-				current->signal |= _S(signr & 0x7f);
-				spin_unlock_irq(&current->sigmask_lock);
+				/* FALLTHRU */
 
+			default:
+				lock_kernel();
+				sigaddset(&current->signal, signr);
 				current->flags |= PF_SIGNALED;
-
-				lock_kernel(); /* 8-( */
-				do_exit(signr);
-				unlock_kernel();
+				do_exit((signr & 0x7f) | core);
+			}
+		} else if (ka->sa.sa_handler == SIG_IGN) {
+			if (signr == SIGCHLD) {
+				/* Check for SIGCHLD: it's special.  */
+				while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
+					/* nothing */;
 			}
+		} else {
+			/* Whee!  Actually deliver the signal.  */
+			handle_signal(signr, ka, &info, oldset, regs);
+			return 1;
 		}
-		handle_signal(signr, sa, oldmask, regs);
-		return 1;
+	skip_signal:
+		spin_lock_irq(&current->sigmask_lock);
 	}
 
 	/* Did we come from a system call? */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov