patch-2.1.48 linux/arch/ppc/kernel/signal.c

Next file: linux/arch/ppc/kernel/strcase.c
Previous file: linux/arch/ppc/kernel/setup.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.47/linux/arch/ppc/kernel/signal.c linux/arch/ppc/kernel/signal.c
@@ -1,9 +1,17 @@
 /*
  *  linux/arch/ppc/kernel/signal.c
  *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- *  Adapted for PowerPC by Gary Thomas
- *  Modified by Cort Dougan (cort@cs.nmt.edu) 
+ *  PowerPC version 
+ *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ *  Derived from "arch/i386/kernel/signal.c"
+ *    Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ *
  */
 
 #include <linux/sched.h>
@@ -22,6 +30,12 @@
 
 #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
 
+#define DEBUG_SIGNALS
+#undef  DEBUG_SIGNALS
+
+#define PAUSE_AFTER_SIGNAL
+#undef  PAUSE_AFTER_SIGNAL
+
 asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
 
 /*
@@ -30,79 +44,85 @@
 asmlinkage int sys_sigsuspend(unsigned long set, int p2, int p3, int p4, int p6, int p7, struct pt_regs *regs)
 {
 	unsigned long mask;
-	int ret = -EINTR;
 
-	lock_kernel();
+	spin_lock_irq(&current->sigmask_lock);
 	mask = current->blocked;
 	current->blocked = set & _BLOCKABLE;
+	spin_unlock_irq(&current->sigmask_lock);
+
 	regs->gpr[3] = -EINTR;
-#if 0
+#ifdef DEBUG_SIGNALS
 printk("Task: %x[%d] - SIGSUSPEND at %x, Mask: %x\n", current, current->pid, regs->nip, set);	
 #endif
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(mask,regs))
-			goto out;
+		if (do_signal(mask,regs)) {
+			/*
+			 * If a signal handler needs to be called,
+			 * do_signal() has set R3 to the signal number (the
+			 * first argument of the signal handler), so don't
+			 * overwrite that with EINTR !
+			 * In the other cases, do_signal() doesn't touch 
+			 * R3, so it's still set to -EINTR (see above).
+			 */
+			return regs->gpr[3];
+		}
 	}
-out:
-	unlock_kernel();
-	return ret;
 }
 
+/*
+ * This sets regs->esp even though we don't actually use sigstacks yet..
+ */
 asmlinkage int sys_sigreturn(struct pt_regs *regs)
 {
 	struct sigcontext_struct *sc;
 	struct pt_regs *int_regs;
 	int signo, ret;
 
-	lock_kernel();
 #if 1 	
 	if (verify_area(VERIFY_READ, (void *) regs->gpr[1], sizeof(sc))
-	    || (regs->gpr[1] >=KERNELBASE))
+	    || (regs->gpr[1] >= KERNELBASE))
 		goto badframe;
 #endif	
-	sc = (struct sigcontext_struct *)regs->gpr[1];
-	current->blocked = sc->oldmask & _BLOCKABLE;
-	int_regs = sc->regs;
-	signo = sc->signal;
-	sc++;  /* Pop signal 'context' */
+	sc = (struct sigcontext_struct *)(regs->gpr[1]+STACK_FRAME_OVERHEAD);
+	get_user(current->blocked, &sc->oldmask);
+	current->blocked &= _BLOCKABLE;
+	get_user(int_regs, &sc->regs);
+	get_user(signo, &sc->signal);
+	sc++;			/* Pop signal 'context' */
+#ifdef DEBUG_SIGNALS
+printk("Sig return - Regs: %x, sc: %x, sig: %d\n", int_regs, sc, signo);
+#endif
 	if (sc == (struct sigcontext_struct *)(int_regs)) {
 		/* Last stacked signal */
-#if 0	
-		/* This doesn't work - it blows away the return address! */
 		memcpy(regs, int_regs, sizeof(*regs));
-#else
-		/* Don't mess up 'my' stack frame */
-		memcpy(&regs->gpr, &int_regs->gpr, sizeof(*regs)-sizeof(regs->_overhead));
-#endif		
-		if ((int)regs->orig_gpr3 >= 0 &&
+		if (regs->trap == 0x0C00 /* System Call! */ &&
 		    ((int)regs->result == -ERESTARTNOHAND ||
 		     (int)regs->result == -ERESTARTSYS ||
-		     (int)regs->result == -ERESTARTNOINTR))
-		{
+		     (int)regs->result == -ERESTARTNOINTR)) {
 			regs->gpr[3] = regs->orig_gpr3;
 			regs->nip -= 4; /* Back up & retry system call */
 			regs->result = 0;
 		}
-		ret = (regs->result);
-	} else { /* More signals to go */
-		regs->gpr[1] = (unsigned long)sc;
-		regs->gpr[3] = sc->signal;
-		regs->gpr[4] = sc->regs;
-		regs->link = (unsigned long)((sc->regs)+1);
-		regs->nip = sc->handler;
-		ret = sc->signal;
+		ret = regs->result;
+	} else {
+		/* More signals to go */
+		regs->gpr[1] = (unsigned long)sc - STACK_FRAME_OVERHEAD;
+		get_user(regs->gpr[3], &sc->signal);
+		get_user(int_regs, (struct pt_regs **) &sc->regs);
+		regs->gpr[4] = (unsigned long) int_regs;
+		regs->link = (unsigned long) (int_regs+1);
+		get_user(regs->nip, &sc->handler);
+		ret = regs->gpr[3];
 	}
-	goto out;
+	return ret;
 
 badframe:
-	/*printk("sys_sigreturn(): badstack regs %x cur %s/%d\n",
-	 regs,current->comm,current->pid);*/
+	lock_kernel();
 	do_exit(SIGSEGV);
-out:
 	unlock_kernel();
-	return ret;
+	return -EFAULT;
 }
 
 
@@ -120,21 +140,24 @@
 	unsigned long mask;
 	unsigned long handler_signal = 0;
 	unsigned long *frame = NULL;
-	unsigned long *trampoline, *regs_ptr;
+	unsigned long *trampoline;
+	unsigned long *regs_ptr;
 	unsigned long nip = 0;
 	unsigned long signr;
 	struct sigcontext_struct *sc;
 	struct sigaction * sa;
-	int bitno, s, ret;
+	int bitno;
 
-	lock_kernel();
 	mask = ~current->blocked;
 	while ((signr = current->signal & mask)) {
+#if 0
+		signr = ffz(~signr);  /* Compute bit # */
+#else
 		for (bitno = 0;  bitno < 32;  bitno++)
 			if (signr & (1<<bitno))
 				break;
 		signr = bitno;
-
+#endif
 		current->signal &= ~(1<<signr);  /* Clear bit */
 		sa = current->sig->action + signr;
 		signr++;
@@ -150,6 +173,8 @@
 				continue;
 			if (_S(signr) & current->blocked) {
 				current->signal |= _S(signr);
+				spin_lock_irq(&current->sigmask_lock);
+				spin_unlock_irq(&current->sigmask_lock);
 				continue;
 			}
 			sa = current->sig->action + signr - 1;
@@ -169,32 +194,45 @@
 			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;
 				current->exit_code = signr;
 				if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags &
-				      SA_NOCLDSTOP))
+						SA_NOCLDSTOP))
 					notify_parent(current);
 				schedule();
 				continue;
 
 			case SIGQUIT: case SIGILL: case SIGTRAP:
 			case SIGIOT: case SIGFPE: case SIGSEGV:
+				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(&current->sigmask_lock);
 				current->signal |= _S(signr & 0x7f);
+				spin_unlock_irq(&current->sigmask_lock);
+
+				current->flags |= PF_SIGNALED;
+
+				lock_kernel(); /* 8-( */
 				do_exit(signr);
+				unlock_kernel();
 			}
 		}
-
-		/* handle signal */
-		if ((int)regs->orig_gpr3 >= 0) {
+		/*
+		 * OK, we're invoking a handler
+		 */
+		if (regs->trap == 0x0C00 /* System Call! */) {
 			if ((int)regs->result == -ERESTARTNOHAND ||
 			    ((int)regs->result == -ERESTARTSYS &&
 			     !(sa->sa_flags & SA_RESTART)))
@@ -203,9 +241,18 @@
 		handler_signal |= 1 << (signr-1);
 		mask &= ~sa->sa_mask;
 	}
-	ret = 0;
-	if (!handler_signal)		/* no handler will be called - return 0 */
-		goto out;
+
+	if (regs->trap == 0x0C00 /* System Call! */ &&
+	    ((int)regs->result == -ERESTARTNOHAND ||
+	     (int)regs->result == -ERESTARTSYS ||
+	     (int)regs->result == -ERESTARTNOINTR)) {
+		regs->gpr[3] = regs->orig_gpr3;
+		regs->nip -= 4; /* Back up & retry system call */
+		regs->result = 0;
+	}
+
+	if (!handler_signal)	/* no handler will be called - return 0 */
+		return 0;
 
 	nip = regs->nip;
 	frame = (unsigned long *) regs->gpr[1];
@@ -213,20 +260,18 @@
 	/* Build trampoline code on stack */
 	frame -= 2;
 	trampoline = frame;
-#if 1
 	/* verify stack is valid for writing regs struct */
 	if (verify_area(VERIFY_WRITE,(void *)frame, sizeof(long)*2+sizeof(*regs))
-	    || (frame >= KERNELBASE ))
+	    || ((unsigned long) frame >= KERNELBASE ))
 		goto badframe;
-#endif
-	trampoline[0] = 0x38007777;  /* li r0,0x7777 */
-	trampoline[1] = 0x44000002;  /* sc           */
+	put_user(0x38007777UL, trampoline);      /* li r0,0x7777 */
+	put_user(0x44000002UL, trampoline+1);    /* sc           */
 	frame -= sizeof(*regs) / sizeof(long);
 	regs_ptr = frame;
-	memcpy(regs_ptr, regs, sizeof(*regs));
+	copy_to_user(regs_ptr, regs, sizeof(*regs));
 	signr = 1;
 	sa = current->sig->action;
-  
+
 	for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
 		if (mask > handler_signal)
 			break;
@@ -234,50 +279,39 @@
 			continue;
 
 		frame -= sizeof(struct sigcontext_struct) / sizeof(long);
-#if 1
 		if (verify_area(VERIFY_WRITE,(void *)frame,
 				sizeof(struct sigcontext_struct)/sizeof(long)))
 			goto badframe;
-#endif    
 		sc = (struct sigcontext_struct *)frame;
 		nip = (unsigned long) sa->sa_handler;
-#if 0 /* Old compiler */		
-		nip = *(unsigned long *)nip;
-#endif		
 		if (sa->sa_flags & SA_ONESHOT)
 			sa->sa_handler = NULL;
-		sc->handler = nip;
-		sc->oldmask = current->blocked;
-		sc->regs = (unsigned long)regs_ptr;
-		sc->signal = signr;
+		put_user(nip, &sc->handler);
+		put_user(oldmask, &sc->oldmask); /* was current->blocked */
+		put_user(regs_ptr, &sc->regs);
+		put_user(signr, &sc->signal);
 		current->blocked |= sa->sa_mask;
 		regs->gpr[3] = signr;
 		regs->gpr[4] = (unsigned long)regs_ptr;
 	}
 	regs->link = (unsigned long)trampoline;
 	regs->nip = nip;
-	regs->gpr[1] = (unsigned long)sc;
+	regs->gpr[1] = (unsigned long)sc - STACK_FRAME_OVERHEAD;
 
-	/* The DATA cache must be flushed here to insure coherency
-	 * between the DATA & INSTRUCTION caches.  Since we just
-	 * created an instruction stream using the DATA [cache] space
-	 * and since the instruction cache will not look in the DATA
-	 * cache for new data, we have to force the data to go on to
-	 * memory and flush the instruction cache to force it to look
-	 * there.  The following function performs this magic
-	 */
-	flush_instruction_cache();
-	ret = 1;
-	goto out;
+	/* The DATA cache must be flushed here to insure coherency */
+	/* between the DATA & INSTRUCTION caches.  Since we just */
+	/* created an instruction stream using the DATA [cache] space */
+	/* and since the instruction cache will not look in the DATA */
+	/* cache for new data, we have to force the data to go on to */
+	/* memory and flush the instruction cache to force it to look */
+	/* there.  The following function performs this magic */
+	store_cache_range((unsigned long) trampoline,
+			  (unsigned long) (trampoline + 2));
+	return 1;
 
 badframe:
-#if 0
-	printk("do_signal(): badstack signr %d frame %x regs %x cur %s/%d\n",
-	       signr, frame, regs, current->comm, current->pid);
-#endif
+	lock_kernel();
 	do_exit(SIGSEGV);
-
-out:
 	unlock_kernel();
-	return ret;
+	return 0;
 }

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