|
From: Andy P. <at...@us...> - 2001-08-17 20:20:45
|
Update of /cvsroot/linux-vax/kernel-2.4/arch/vax/kernel
In directory usw-pr-cvs1:/tmp/cvs-serv18613/kernel
Modified Files:
syscall.c signal.c
Log Message:
Signals implementation. First attempt.
Index: syscall.c
===================================================================
RCS file: /cvsroot/linux-vax/kernel-2.4/arch/vax/kernel/syscall.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- syscall.c 2001/07/31 17:28:26 1.4
+++ syscall.c 2001/08/17 20:20:41 1.5
@@ -95,6 +95,7 @@
by the user.
We'll sort this out later... - KPH 2000-10-16 */
+ /* atp aug 2001, FIXME: ptrace/strace syscall stuff */
__asm__(" pushl %1 \n
subl2 %2,sp \n
@@ -115,8 +116,21 @@
"g"(-EFAULT)
: "r0","r1","r2","r3","r4","r5" /* clobbers*/ );
-/* printk("syscall %d returned %ld (0x%08lx)\n", chmk_arg, regs->r0, regs->r0);*/
+// printk("syscall %d returned %ld (0x%08lx)\n", chmk_arg, regs->r0, regs->r0);
+ /* now we check for softirqs ready to be run */
+ /* This is the equivalent of ENTRY(ret_from_syscall) on other ports */
+ if ( (softirq_active(smp_processor_id()) & softirq_mask(smp_processor_id())) !=0 ) do_softirq();
+
+ /* check for need_reschedule */
+ if (current->need_resched !=0) schedule();
+
+ /* check for pending signals */
+ if (current->sigpending != 0) {
+ /* FIXME: do we need to check the IPL here (i386 does a sti here) */
+ do_signal();
+ }
+
return;
}
Index: signal.c
===================================================================
RCS file: /cvsroot/linux-vax/kernel-2.4/arch/vax/kernel/signal.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- signal.c 2001/07/31 17:28:26 1.3
+++ signal.c 2001/08/17 20:20:41 1.4
@@ -1,14 +1,20 @@
-/*
- $Id$
-
- This file contains the standard functions that the arch-independent
- kernel expects for signal handling
-
- Copyright, 1998-2001 atp, kenn, airlied.
-
- atp Jul 2001, signal handling, based on S390/Intel version.
-
-*/
+/*
+ * linux/arch/vax/kernel/signal.c
+ *
+ * From arch/cris/kernel/signal.c
+ *
+ * Based on arch/i386/kernel/signal.c by
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson *
+ *
+ * Ideas also taken from arch/arm.
+ *
+ * Copyright (C) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bj...@ax...)
+ * VAX port at...@pe....
+ * See syscall.c for details of the call stack layout etc...
+ */
#include <linux/sched.h>
#include <linux/mm.h>
@@ -21,12 +27,20 @@
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
+
+#include <asm/processor.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
+#define DEBUG_SIG 0
+
+/* FIXME: Check this & fixup other regs, like r0 */
+#define RESTART_VAX_SYSCALL(regs) { (regs)->pc -= 4; }
+
+
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset));
+int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs);
int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from)
{
@@ -66,8 +80,8 @@
/*
* Atomically swap in the new signal mask, and wait for a signal.
*/
-asmlinkage int
-sys_sigsuspend(struct pt_regs * regs,int history0, int history1, old_sigset_t mask)
+int
+sys_sigsuspend(struct pt_regs *regs, old_sigset_t mask)
{
sigset_t saveset;
@@ -77,18 +91,22 @@
siginitset(¤t->blocked, mask);
recalc_sigpending(current);
spin_unlock_irq(¤t->sigmask_lock);
- regs->r2 = -EINTR;
+ regs->r0 = -EINTR;
while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
+ current->state = TASK_INTERRUPTIBLE;
schedule();
- if (do_signal(regs, &saveset))
+ if (do_signal(0, &saveset, regs))
return -EINTR;
}
}
-asmlinkage int
-sys_rt_sigsuspend(struct pt_regs * regs,sigset_t *unewset, size_t sigsetsize)
+/*
+ * atp - it is a little confusing, looking at other ports, as to what the arguments to
+ * this function are. I'm assuming two args, plus our pushed pt_regs set up by syscall
+ */
+int
+sys_rt_sigsuspend(struct pt_regs *regs,sigset_t *unewset, size_t sigsetsize)
{
sigset_t saveset, newset;
@@ -105,17 +123,17 @@
current->blocked = newset;
recalc_sigpending(current);
spin_unlock_irq(¤t->sigmask_lock);
- regs->r2 = -EINTR;
+ regs->r0 = -EINTR;
while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
+ current->state = TASK_INTERRUPTIBLE;
schedule();
- if (do_signal(regs, &saveset))
+ if (do_signal(0, &saveset, regs))
return -EINTR;
}
}
-asmlinkage int
+int
sys_sigaction(int sig, const struct old_sigaction *act,
struct old_sigaction *oact)
{
@@ -146,10 +164,12 @@
return ret;
}
-asmlinkage int
-sys_sigaltstack(const stack_t *uss, stack_t *uoss, struct pt_regs *regs)
+
+int
+sys_sigaltstack(const stack_t *uss, stack_t *uoss)
{
- return do_sigaltstack(uss, uoss, regs->sp); /* is ->sp the right one FIXME: check */
+ struct pt_regs *regs = (struct pt_regs *) &uss;
+ return do_sigaltstack(uss, uoss, regs->sp );
}
@@ -158,12 +178,14 @@
*/
struct sigframe {
+ char *pretcode;
struct sigcontext sc;
unsigned long extramask[_NSIG_WORDS-1];
unsigned char retcode[8]; /* trampoline code */
};
struct rt_sigframe {
+ char *pretcode;
struct siginfo *pinfo;
void *puc;
struct siginfo info;
@@ -171,16 +193,462 @@
unsigned char retcode[8]; /* trampoline code */
};
+
+static int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
+{
+ unsigned int err = 0;
+ unsigned long old_usp;
+
+ /* restore the regs from &sc->regs (same as sc, since regs is first)
+ * (sc is already checked for VERIFY_READ since the sigframe was
+ * checked in sys_sigreturn previously)
+ */
+
+ if (__copy_from_user(regs, sc, sizeof(struct pt_regs)))
+ goto badframe;
+
+ /* FIXME: check user mode flag in restored regs PSW */
+
+ /* restore the old USP as it was before we stacked the sc etc.
+ * (we cannot just pop the sigcontext since we aligned the sp and
+ * stuff after pushing it)
+ */
+
+ /* FIXME: check process stack */
+
+ /* TODO: the other ports use regs->orig_XX to disable syscall checks
+ * after this completes, but we don't use that mechanism. maybe we can
+ * use it now ?
+ */
+
+ return err;
+
+badframe:
+ return 1;
+}
+
+asmlinkage int sys_sigreturn(struct pt_regs *regs)
+{
+ struct sigframe *frame = (struct sigframe *)(regs->sp);
+ sigset_t set;
+
+ /*
+ * Since we stacked the signal on a dword boundary,
+ * then frame should be dword aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (((long)frame) & 3)
+ goto badframe;
+
+ 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(¤t->sigmask_lock);
+ current->blocked = set;
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+
+ if (restore_sigcontext(regs, &frame->sc))
+ goto badframe;
+
+ /* TODO: SIGTRAP when single-stepping as in arm ? */
+
+ return regs->r0;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
+{
+ struct rt_sigframe *frame = (struct rt_sigframe *)(regs->sp);
+ sigset_t set;
+ stack_t st;
+
+ /*
+ * Since we stacked the signal on a dword boundary,
+ * then frame should be dword aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (((long)frame) & 3)
+ goto badframe;
+
+ 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(¤t->sigmask_lock);
+ current->blocked = set;
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+
+ if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+ goto badframe;
+
+ if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st)))
+ goto badframe;
+ /* It is more difficult to avoid calling this function than to
+ call it and ignore errors. */
+ do_sigaltstack(&st, NULL, (regs->sp));
+
+ return regs->r0;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/*
+ * Set up a signal frame.
+ */
+
+static int
+setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, unsigned long mask)
+{
+ int err = 0;
+
+ /* copy the regs. they are first in sc so we can use sc directly */
+
+ err |= __copy_to_user(sc, regs, sizeof(struct pt_regs));
+
+ /* then some other stuff */
+
+ err |= __put_user(mask, &sc->oldmask);
+
+ return err;
+}
+
+/* figure out where we want to put the new signal frame - usually on the stack */
-int sys_sigreturn(void)
+static inline void *
+get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
{
- return -ENOSYS;
+ unsigned long sp = regs->sp;
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa.sa_flags & SA_ONSTACK) {
+ if (! on_sig_stack(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+ }
+
+ /* make sure the frame is dword-aligned */
+
+ sp &= ~3;
+
+ return (void *)(sp - frame_size);
}
+
+/* grab and setup a signal frame.
+ *
+ * basically we stack a lot of state info, and arrange for the
+ * user-mode program to return to the kernel using either a
+ * trampoline which performs the syscall sigreturn, or a provided
+ * user-mode trampoline.
+ */
-int sys_rt_sigreturn(void)
+static void setup_frame(int sig, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs * regs)
{
- return -ENOSYS;
+ struct sigframe *frame;
+ unsigned long return_ip;
+ int err = 0;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
+ if (err)
+ goto give_sigsegv;
+
+ if (_NSIG_WORDS > 1) {
+ err |= __copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask));
+ }
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ return_ip = (unsigned long)ka->sa.sa_restorer;
+ } else {
+ /* trampoline - the desired return ip is the retcode itself */
+ return_ip = (unsigned long)&frame->retcode;
+ /* This is chmk __NR_sigreturn; */
+ /* TODO: check byteorder */
+ err |= __put_user(0xbc8f, (short *)(frame->retcode+0));
+ err |= __put_user(__NR_sigreturn, (short *)(frame->retcode+2));
+ }
+
+ err |= __put_user(return_ip, &frame->pretcode);
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+
+ regs->pc = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */
+ regs->sp = frame;
+
+ return;
+
+give_sigsegv:
+ if (sig == SIGSEGV)
+ ka->sa.sa_handler = SIG_DFL;
+ force_sig(SIGSEGV, current);
}
+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;
+ unsigned long return_ip;
+ int err = 0;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ err |= __put_user(&frame->info, &frame->pinfo);
+ err |= __put_user(&frame->uc, &frame->puc);
+ err |= copy_siginfo_to_user(&frame->info, info);
+ if (err)
+ goto give_sigsegv;
+
+ /* Clear all the bits of the ucontext we don't use. */
+ err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
+
+ err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]);
+
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ return_ip = (unsigned long)ka->sa.sa_restorer;
+ } else {
+ /* trampoline - the desired return ip is the retcode itself */
+ return_ip = (unsigned long)&frame->retcode;
+ /* TODO: check byteorder */
+ err |= __put_user(0xbc8f, (short *)(frame->retcode+0));
+ err |= __put_user(__NR_sigreturn, (short *)(frame->retcode+2));
+ }
+
+ err |= __put_user(return_ip, &frame->pretcode);
+ if (err)
+ goto give_sigsegv;
+
+ /* TODO what is the current->exec_domain stuff and invmap ? */
+
+ /* Set up registers for signal handler */
+
+ regs->pc = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */
+ regs->sp = frame; /* what we enter LATER */
+
+ return;
+give_sigsegv:
+ if (sig == SIGSEGV)
+ ka->sa.sa_handler = SIG_DFL;
+ force_sig(SIGSEGV, current);
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+static inline void
+handle_signal(int canrestart, unsigned long sig, struct k_sigaction *ka,
+ siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
+{
+ /* Are we from a system call? */
+ if (canrestart) {
+ /* If so, check system call restarting.. */
+ switch (regs->r0) {
+ case -ERESTARTNOHAND:
+ /* ERESTARTNOHAND means that the syscall should only be
+ restarted if there was no handler for the signal, and since
+ we only get here if there is a handler, we dont restart */
+ regs->r0 = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ /* ERESTARTSYS means to restart the syscall if there is no
+ handler or the handler was registered with SA_RESTART */
+ if (!(ka->sa.sa_flags & SA_RESTART)) {
+ regs->r0 = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ /* ERESTARTNOINTR means that the syscall should be called again
+ after the signal handler returns. */
+ RESTART_VAX_SYSCALL(regs);
+ }
+ }
+
+ /* 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 (ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
+
+ if (!(ka->sa.sa_flags & SA_NODEFER)) {
+ spin_lock_irq(¤t->sigmask_lock);
+ sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
+ sigaddset(¤t->blocked,sig);
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+ }
+}
+
+/*
+ * 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.
+ */
+int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs)
+{
+ siginfo_t info;
+ struct k_sigaction *ka;
+
+ /*
+ * 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 (!user_mode(regs))
+ return 1;
+
+ if (!oldset)
+ oldset = ¤t->blocked;
+
+ for (;;) {
+ unsigned long signr;
+
+ spin_lock_irq(¤t->sigmask_lock);
+ signr = dequeue_signal(¤t->blocked, &info);
+ spin_unlock_irq(¤t->sigmask_lock);
+
+ if (!signr)
+ break;
+
+ if ((current->ptrace & PT_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;
+ current->exit_code = 0;
+
+ /* The debugger continued. Ignore SIGSTOP. */
+ if (signr == SIGSTOP)
+ continue;
+
+ /* 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;
+ }
+ }
+
+ 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)
+ /* nothing */;
+ continue;
+ }
+
+ if (ka->sa.sa_handler == SIG_DFL) {
+ int exit_code = signr;
+
+ /* Init gets no signals it doesn't want. */
+ if (current->pid == 1)
+ continue;
+
+ switch (signr) {
+ case SIGCONT: case SIGCHLD: case SIGWINCH:
+ continue;
+
+ case SIGTSTP: case SIGTTIN: case SIGTTOU:
+ if (is_orphaned_pgrp(current->pgrp))
+ continue;
+ /* FALLTHRU */
+
+ case SIGSTOP:
+ current->state = TASK_STOPPED;
+ current->exit_code = signr;
+ 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 SIGABRT: case SIGFPE: case SIGSEGV:
+ case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ:
+ if (do_coredump(signr, regs))
+ exit_code |= 0x80;
+ /* FALLTHRU */
+
+ default:
+ lock_kernel();
+ sigaddset(¤t->pending.signal, signr);
+ recalc_sigpending(current);
+ current->flags |= PF_SIGNALED;
+ do_exit(exit_code);
+ /* NOTREACHED */
+ }
+ }
+
+ /* Whee! Actually deliver the signal. */
+ handle_signal(canrestart, signr, ka, &info, oldset, regs);
+ return 1;
+ }
+
+ /* Did we come from a system call? */
+ if (canrestart) {
+ /* Restart the system call - no handlers present */
+ if (regs->r0 == -ERESTARTNOHAND ||
+ regs->r0 == -ERESTARTSYS ||
+ regs->r0 == -ERESTARTNOINTR) {
+ RESTART_VAX_SYSCALL(regs);
+ }
+ }
+ return 0;
+}
|