patch-2.4.13 linux/arch/arm/kernel/signal.c
Next file: linux/arch/arm/kernel/time.c
Previous file: linux/arch/arm/kernel/setup.c
Back to the patch index
Back to the overall index
- Lines: 352
- Date:
Thu Oct 11 09:04:57 2001
- Orig file:
v2.4.12/linux/arch/arm/kernel/signal.c
- Orig date:
Mon Aug 27 12:41:38 2001
diff -u --recursive --new-file v2.4.12/linux/arch/arm/kernel/signal.c linux/arch/arm/kernel/signal.c
@@ -1,7 +1,7 @@
/*
* linux/arch/arm/kernel/signal.c
*
- * Copyright (C) 1995, 1996 Russell King
+ * Copyright (C) 1995-2001 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -19,7 +19,9 @@
#include <linux/ptrace.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
+#include <linux/personality.h>
#include <linux/tty.h>
+#include <linux/elf.h>
#include <asm/pgalloc.h>
#include <asm/ucontext.h>
@@ -29,8 +31,23 @@
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-#define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn))
-#define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn))
+/*
+ * For ARM syscalls, we encode the syscall number into the instruction.
+ */
+#define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn))
+#define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn))
+
+/*
+ * For Thumb syscalls, we pass the syscall number via r7. We therefore
+ * need two 16-bit instructions.
+ */
+#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_sigreturn - __NR_SYSCALL_BASE))
+#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE))
+
+static const unsigned long retcodes[4] = {
+ SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN,
+ SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN
+};
asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall);
@@ -208,11 +225,11 @@
sigset_t set;
/*
- * Since we stacked the signal on a word boundary,
+ * Since we stacked the signal on a 64-bit boundary,
* then 'sp' should be word aligned here. If it's
* not, then the user is trying to mess with us.
*/
- if (regs->ARM_sp & 3)
+ if (regs->ARM_sp & 7)
goto badframe;
frame = (struct sigframe *)regs->ARM_sp;
@@ -251,11 +268,11 @@
sigset_t set;
/*
- * Since we stacked the signal on a word boundary,
+ * Since we stacked the signal on a 64-bit boundary,
* then 'sp' should be word aligned here. If it's
* not, then the user is trying to mess with us.
*/
- if (regs->ARM_sp & 3)
+ if (regs->ARM_sp & 7)
goto badframe;
frame = (struct rt_sigframe *)regs->ARM_sp;
@@ -319,8 +336,8 @@
return err;
}
-static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
- unsigned long framesize)
+static inline void *
+get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize)
{
unsigned long sp = regs->ARM_sp;
@@ -331,77 +348,103 @@
sp = current->sas_ss_sp + current->sas_ss_size;
/*
- * No matter what happens, 'sp' must be word
- * aligned otherwise nasty things could happen
+ * ATPCS B01 mandates 8-byte alignment
*/
- /* ATPCS B01 mandates 8-byte alignment */
return (void *)((sp - framesize) & ~7);
}
-static void setup_frame(int sig, struct k_sigaction *ka,
- sigset_t *set, struct pt_regs *regs)
+static int
+setup_return(struct pt_regs *regs, struct k_sigaction *ka,
+ unsigned long *rc, void *frame, int usig)
{
- struct sigframe *frame;
+ unsigned long handler = (unsigned long)ka->sa.sa_handler;
unsigned long retcode;
- int err = 0;
-
- frame = get_sigframe(ka, regs, sizeof(*frame));
-
- if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
- goto segv_and_exit;
+ int thumb = 0;
+#ifdef CONFIG_CPU_32
+ unsigned long cpsr = regs->ARM_cpsr;
- err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]);
+ /*
+ * Maybe we need to deliver a 32-bit signal to a 26-bit task.
+ */
+ if (ka->sa.sa_flags & SA_THIRTYTWO)
+ cpsr = (cpsr & ~MODE_MASK) | USR_MODE;
- if (_NSIG_WORDS > 1) {
- err |= __copy_to_user(frame->extramask, &set->sig[1],
- sizeof(frame->extramask));
+#ifdef CONFIG_ARM_THUMB
+ if (elf_hwcap & HWCAP_THUMB) {
+ /*
+ * The LSB of the handler determines if we're going to
+ * be using THUMB or ARM mode for this signal handler.
+ */
+ thumb = handler & 1;
+
+ if (thumb)
+ cpsr |= T_BIT;
+ else
+ cpsr &= ~T_BIT;
}
+#endif
+#endif
- /* Set up to return from userspace. If provided, use a stub
- already in userspace. */
if (ka->sa.sa_flags & SA_RESTORER) {
retcode = (unsigned long)ka->sa.sa_restorer;
} else {
- retcode = (unsigned long)&frame->retcode;
- __put_user_error(SWI_SYS_SIGRETURN, &frame->retcode, err);
- flush_icache_range(retcode, retcode + 4);
- }
+ unsigned int idx = thumb;
- if (err)
- goto segv_and_exit;
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ idx += 2;
- if (current->exec_domain && current->exec_domain->signal_invmap && sig < 32)
- regs->ARM_r0 = current->exec_domain->signal_invmap[sig];
- else
- regs->ARM_r0 = sig;
+ if (__put_user(retcodes[idx], rc))
+ return 1;
+
+ flush_icache_range((unsigned long)rc,
+ (unsigned long)(rc + 1));
+
+ retcode = ((unsigned long)rc) + thumb;
+ }
+
+ regs->ARM_r0 = usig;
regs->ARM_sp = (unsigned long)frame;
regs->ARM_lr = retcode;
- regs->ARM_pc = (unsigned long)ka->sa.sa_handler;
-#if defined(CONFIG_CPU_32)
- /* Maybe we need to deliver a 32-bit signal to a 26-bit task. */
- if (ka->sa.sa_flags & SA_THIRTYTWO)
- regs->ARM_cpsr = USR_MODE;
+ regs->ARM_pc = handler & (thumb ? ~1 : ~3);
+
+#ifdef CONFIG_CPU_32
+ regs->ARM_cpsr = cpsr;
#endif
- if (valid_user_regs(regs))
- return;
-segv_and_exit:
- if (sig == SIGSEGV)
- ka->sa.sa_handler = SIG_DFL;
- force_sig(SIGSEGV, current);
+ return 0;
}
-static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
- sigset_t *set, struct pt_regs *regs)
+static int
+setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs)
{
- struct rt_sigframe *frame;
- unsigned long retcode;
+ struct sigframe *frame = get_sigframe(ka, regs, sizeof(*frame));
int err = 0;
- frame = get_sigframe(ka, regs, sizeof(struct rt_sigframe));
+ if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
+ return 1;
+
+ err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]);
+
+ if (_NSIG_WORDS > 1) {
+ err |= __copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask));
+ }
+
+ if (err == 0)
+ err = setup_return(regs, ka, &frame->retcode, frame, usig);
+
+ return err;
+}
+
+static int
+setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct rt_sigframe *frame = get_sigframe(ka, regs, sizeof(*frame));
+ int err = 0;
if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
- goto segv_and_exit;
+ return 1;
__put_user_error(&frame->info, &frame->pinfo, err);
__put_user_error(&frame->uc, &frame->puc, err);
@@ -414,47 +457,20 @@
regs, set->sig[0]);
err |= __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) {
- retcode = (unsigned long)ka->sa.sa_restorer;
- } else {
- retcode = (unsigned long)&frame->retcode;
- __put_user_error(SWI_SYS_RT_SIGRETURN, &frame->retcode, err);
- flush_icache_range(retcode, retcode + 4);
- }
-
- if (err)
- goto segv_and_exit;
-
- if (current->exec_domain && current->exec_domain->signal_invmap && sig < 32)
- regs->ARM_r0 = current->exec_domain->signal_invmap[sig];
- else
- regs->ARM_r0 = sig;
+ if (err == 0)
+ err = setup_return(regs, ka, &frame->retcode, frame, usig);
- /*
- * For realtime signals we must also set the second and third
- * arguments for the signal handler.
- * -- Peter Maydell <pmaydell@chiark.greenend.org.uk> 2000-12-06
- */
- regs->ARM_r1 = (unsigned long)frame->pinfo;
- regs->ARM_r2 = (unsigned long)frame->puc;
-
- regs->ARM_sp = (unsigned long)frame;
- regs->ARM_lr = retcode;
- regs->ARM_pc = (unsigned long)ka->sa.sa_handler;
-#if defined(CONFIG_CPU_32)
- /* Maybe we need to deliver a 32-bit signal to a 26-bit task. */
- if (ka->sa.sa_flags & SA_THIRTYTWO)
- regs->ARM_cpsr = USR_MODE;
-#endif
- if (valid_user_regs(regs))
- return;
+ if (err == 0) {
+ /*
+ * For realtime signals we must also set the second and third
+ * arguments for the signal handler.
+ * -- Peter Maydell <pmaydell@chiark.greenend.org.uk> 2000-12-06
+ */
+ regs->ARM_r1 = (unsigned long)frame->pinfo;
+ regs->ARM_r2 = (unsigned long)frame->puc;
+ }
-segv_and_exit:
- if (sig == SIGSEGV)
- ka->sa.sa_handler = SIG_DFL;
- force_sig(SIGSEGV, current);
+ return err;
}
/*
@@ -464,22 +480,47 @@
handle_signal(unsigned long sig, struct k_sigaction *ka,
siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
{
- /* Set up the stack frame */
+ struct task_struct *tsk = current;
+ int usig = sig;
+ int ret;
+
+ /*
+ * translate the signal
+ */
+ if (usig < 32 && tsk->exec_domain && tsk->exec_domain->signal_invmap)
+ usig = tsk->exec_domain->signal_invmap[usig];
+
+ /*
+ * Set up the stack frame
+ */
if (ka->sa.sa_flags & SA_SIGINFO)
- setup_rt_frame(sig, ka, info, oldset, regs);
+ ret = setup_rt_frame(usig, ka, info, oldset, regs);
else
- setup_frame(sig, ka, oldset, regs);
+ ret = setup_frame(usig, ka, oldset, regs);
- if (ka->sa.sa_flags & SA_ONESHOT)
- ka->sa.sa_handler = SIG_DFL;
+ /*
+ * Check that the resulting registers are actually sane.
+ */
+ ret |= !valid_user_regs(regs);
- 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);
+ if (ret == 0) {
+ if (ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
+
+ if (!(ka->sa.sa_flags & SA_NODEFER)) {
+ spin_lock_irq(&tsk->sigmask_lock);
+ sigorsets(&tsk->blocked, &tsk->blocked,
+ &ka->sa.sa_mask);
+ sigaddset(&tsk->blocked, sig);
+ recalc_sigpending(tsk);
+ spin_unlock_irq(&tsk->sigmask_lock);
+ }
+ return;
}
+
+ if (sig == SIGSEGV)
+ ka->sa.sa_handler = SIG_DFL;
+ force_sig(SIGSEGV, tsk);
}
/*
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)