📄 signal.c
字号:
/* * linux/arch/parisc/kernel/signal.c: Architecture-specific signal * handling support. * * Copyright (C) 2000 David Huggins-Daines <dhd@debian.org> * Copyright (C) 2000 Linuxcare, Inc. * * Based on the ia64, i386, and alpha versions. * * Like the IA-64, we are a recent enough port (we are *starting* * with glibc2.2) that we do not need to support the old non-realtime * Linux signals. Therefore we don't. HP/UX signals will go in * arch/parisc/hpux/signal.c when we figure out how to do them. */#include <linux/sched.h>#include <linux/mm.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/kernel.h>#include <linux/signal.h>#include <linux/errno.h>#include <linux/wait.h>#include <linux/ptrace.h>#include <linux/unistd.h>#include <linux/stddef.h>#include <linux/compat.h>#include <linux/elf.h>#include <linux/personality.h>#include <asm/ucontext.h>#include <asm/rt_sigframe.h>#include <asm/uaccess.h>#include <asm/pgalloc.h>#include <asm/cacheflush.h>#include <asm/asm-offsets.h>#ifdef CONFIG_COMPAT#include <linux/compat.h>#include "signal32.h"#endif#define DEBUG_SIG 0 #define DEBUG_SIG_LEVEL 2#if DEBUG_SIG#define DBG(LEVEL, ...) \ ((DEBUG_SIG_LEVEL >= LEVEL) \ ? printk(__VA_ARGS__) : (void) 0)#else#define DBG(LEVEL, ...)#endif #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))/* gcc will complain if a pointer is cast to an integer of different * size. If you really need to do this (and we do for an ELF32 user * application in an ELF64 kernel) then you have to do a cast to an * integer of the same size first. The A() macro accomplishes * this. */#define A(__x) ((unsigned long)(__x))int do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall);/* * Atomically swap in the new signal mask, and wait for a signal. */#ifdef __LP64__#include "sys32.h"#endifasmlinkage intsys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, struct pt_regs *regs){ sigset_t saveset, newset;#ifdef __LP64__ compat_sigset_t newset32; if(personality(current->personality) == PER_LINUX32){ /* XXX: Don't preclude handling different sized sigset_t's. */ if (sigsetsize != sizeof(compat_sigset_t)) return -EINVAL; if (copy_from_user(&newset32, (compat_sigset_t __user *)unewset, sizeof(newset32))) return -EFAULT; sigset_32to64(&newset,&newset32); } else #endif { /* 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(¤t->sighand->siglock); saveset = current->blocked; current->blocked = newset; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); regs->gr[28] = -EINTR; while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); if (do_signal(&saveset, regs, 1)) return -EINTR; }}/* * Do a signal return - restore sigcontext. *//* Trampoline for calling rt_sigreturn() */#define INSN_LDI_R25_0 0x34190000 /* ldi 0,%r25 (in_syscall=0) */#define INSN_LDI_R25_1 0x34190002 /* ldi 1,%r25 (in_syscall=1) */#define INSN_LDI_R20 0x3414015a /* ldi __NR_rt_sigreturn,%r20 */#define INSN_BLE_SR2_R0 0xe4008200 /* be,l 0x100(%sr2,%r0),%sr0,%r31 */#define INSN_NOP 0x08000240 /* nop *//* For debugging */#define INSN_DIE_HORRIBLY 0x68000ccc /* stw %r0,0x666(%sr0,%r0) */static longrestore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs){ long err = 0; err |= __copy_from_user(regs->gr, sc->sc_gr, sizeof(regs->gr)); err |= __copy_from_user(regs->fr, sc->sc_fr, sizeof(regs->fr)); err |= __copy_from_user(regs->iaoq, sc->sc_iaoq, sizeof(regs->iaoq)); err |= __copy_from_user(regs->iasq, sc->sc_iasq, sizeof(regs->iasq)); err |= __get_user(regs->sar, &sc->sc_sar); DBG(2,"restore_sigcontext: iaoq is 0x%#lx / 0x%#lx\n", regs->iaoq[0],regs->iaoq[1]); DBG(2,"restore_sigcontext: r28 is %ld\n", regs->gr[28]); return err;}voidsys_rt_sigreturn(struct pt_regs *regs, int in_syscall){ struct rt_sigframe __user *frame; struct siginfo si; sigset_t set; unsigned long usp = (regs->gr[30] & ~(0x01UL)); unsigned long sigframe_size = PARISC_RT_SIGFRAME_SIZE;#ifdef __LP64__ compat_sigset_t compat_set; struct compat_rt_sigframe __user * compat_frame; if(personality(current->personality) == PER_LINUX32) sigframe_size = PARISC_RT_SIGFRAME_SIZE32;#endif /* Unwind the user stack to get the rt_sigframe structure. */ frame = (struct rt_sigframe __user *) (usp - sigframe_size); DBG(2,"sys_rt_sigreturn: frame is %p\n", frame);#ifdef __LP64__ compat_frame = (struct compat_rt_sigframe __user *)frame; if(personality(current->personality) == PER_LINUX32){ DBG(2,"sys_rt_sigreturn: ELF32 process.\n"); if (__copy_from_user(&compat_set, &compat_frame->uc.uc_sigmask, sizeof(compat_set))) goto give_sigsegv; sigset_32to64(&set,&compat_set); } else#endif { if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) goto give_sigsegv; } sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sighand->siglock); current->blocked = set; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); /* Good thing we saved the old gr[30], eh? */#ifdef __LP64__ if(personality(current->personality) == PER_LINUX32){ DBG(1,"sys_rt_sigreturn: compat_frame->uc.uc_mcontext 0x%p\n", &compat_frame->uc.uc_mcontext);// FIXME: Load upper half from register file if (restore_sigcontext32(&compat_frame->uc.uc_mcontext, &compat_frame->regs, regs)) goto give_sigsegv; DBG(1,"sys_rt_sigreturn: usp %#08lx stack 0x%p\n", usp, &compat_frame->uc.uc_stack); if (do_sigaltstack32(&compat_frame->uc.uc_stack, NULL, usp) == -EFAULT) goto give_sigsegv; } else#endif { DBG(1,"sys_rt_sigreturn: frame->uc.uc_mcontext 0x%p\n", &frame->uc.uc_mcontext); if (restore_sigcontext(&frame->uc.uc_mcontext, regs)) goto give_sigsegv; DBG(1,"sys_rt_sigreturn: usp %#08lx stack 0x%p\n", usp, &frame->uc.uc_stack); if (do_sigaltstack(&frame->uc.uc_stack, NULL, usp) == -EFAULT) goto give_sigsegv; } /* If we are on the syscall path IAOQ will not be restored, and * if we are on the interrupt path we must not corrupt gr31. */ if (in_syscall) regs->gr[31] = regs->iaoq[0];#if DEBUG_SIG DBG(1,"sys_rt_sigreturn: returning to %#lx, DUMPING REGS:\n", regs->iaoq[0]); show_regs(regs);#endif return;give_sigsegv: DBG(1,"sys_rt_sigreturn: Sending SIGSEGV\n"); si.si_signo = SIGSEGV; si.si_errno = 0; si.si_code = SI_KERNEL; si.si_pid = current->pid; si.si_uid = current->uid; si.si_addr = &frame->uc; force_sig_info(SIGSEGV, &si, current); return;}/* * Set up a signal frame. */static inline void __user *get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size){ /*FIXME: ELF32 vs. ELF64 has different frame_size, but since we don't use the parameter it doesn't matter */ DBG(1,"get_sigframe: ka = %#lx, sp = %#lx, frame_size = %#lx\n", (unsigned long)ka, sp, frame_size); if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) sp = current->sas_ss_sp; /* Stacks grow up! */ DBG(1,"get_sigframe: Returning sp = %#lx\n", (unsigned long)sp); return (void __user *) sp; /* Stacks grow up. Fun. */}static longsetup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, int in_syscall) { unsigned long flags = 0; long err = 0; if (on_sig_stack((unsigned long) sc)) flags |= PARISC_SC_FLAG_ONSTACK; if (in_syscall) { flags |= PARISC_SC_FLAG_IN_SYSCALL; /* regs->iaoq is undefined in the syscall return path */ err |= __put_user(regs->gr[31], &sc->sc_iaoq[0]); err |= __put_user(regs->gr[31]+4, &sc->sc_iaoq[1]); err |= __put_user(regs->sr[3], &sc->sc_iasq[0]); err |= __put_user(regs->sr[3], &sc->sc_iasq[1]); DBG(1,"setup_sigcontext: iaoq %#lx / %#lx (in syscall)\n", regs->gr[31], regs->gr[31]+4); } else { err |= __copy_to_user(sc->sc_iaoq, regs->iaoq, sizeof(regs->iaoq)); err |= __copy_to_user(sc->sc_iasq, regs->iasq, sizeof(regs->iasq)); DBG(1,"setup_sigcontext: iaoq %#lx / %#lx (not in syscall)\n", regs->iaoq[0], regs->iaoq[1]); } err |= __put_user(flags, &sc->sc_flags); err |= __copy_to_user(sc->sc_gr, regs->gr, sizeof(regs->gr)); err |= __copy_to_user(sc->sc_fr, regs->fr, sizeof(regs->fr)); err |= __put_user(regs->sar, &sc->sc_sar); DBG(1,"setup_sigcontext: r28 is %ld\n", regs->gr[28]); return err;}static longsetup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs, int in_syscall){ struct rt_sigframe __user *frame; unsigned long rp, usp; unsigned long haddr, sigframe_size; int err = 0;#ifdef __LP64__ compat_int_t compat_val; struct compat_rt_sigframe __user * compat_frame; compat_sigset_t compat_set;#endif usp = (regs->gr[30] & ~(0x01UL)); /*FIXME: frame_size parameter is unused, remove it. */ frame = get_sigframe(ka, usp, sizeof(*frame)); DBG(1,"SETUP_RT_FRAME: START\n"); DBG(1,"setup_rt_frame: frame %p info %p\n", frame, info); #ifdef __LP64__ compat_frame = (struct compat_rt_sigframe __user *)frame; if(personality(current->personality) == PER_LINUX32) { DBG(1,"setup_rt_frame: frame->info = 0x%p\n", &compat_frame->info); err |= compat_copy_siginfo_to_user(&compat_frame->info, info); DBG(1,"SETUP_RT_FRAME: 1\n"); compat_val = (compat_int_t)current->sas_ss_sp; err |= __put_user(compat_val, &compat_frame->uc.uc_stack.ss_sp); DBG(1,"SETUP_RT_FRAME: 2\n"); compat_val = (compat_int_t)current->sas_ss_size; err |= __put_user(compat_val, &compat_frame->uc.uc_stack.ss_size); DBG(1,"SETUP_RT_FRAME: 3\n"); compat_val = sas_ss_flags(regs->gr[30]); err |= __put_user(compat_val, &compat_frame->uc.uc_stack.ss_flags); DBG(1,"setup_rt_frame: frame->uc = 0x%p\n", &compat_frame->uc);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -