162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Signal handling 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu> 562306a36Sopenharmony_ci * Copyright (C) 2008-2009 PetaLogix 662306a36Sopenharmony_ci * Copyright (C) 2003,2004 John Williams <jwilliams@itee.uq.edu.au> 762306a36Sopenharmony_ci * Copyright (C) 2001 NEC Corporation 862306a36Sopenharmony_ci * Copyright (C) 2001 Miles Bader <miles@gnu.org> 962306a36Sopenharmony_ci * Copyright (C) 1999,2000 Niibe Yutaka & Kaz Kojima 1062306a36Sopenharmony_ci * Copyright (C) 1991,1992 Linus Torvalds 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * This file was derived from the sh version, arch/sh/kernel/signal.c 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General 1762306a36Sopenharmony_ci * Public License. See the file COPYING in the main directory of this 1862306a36Sopenharmony_ci * archive for more details. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/sched.h> 2262306a36Sopenharmony_ci#include <linux/mm.h> 2362306a36Sopenharmony_ci#include <linux/smp.h> 2462306a36Sopenharmony_ci#include <linux/kernel.h> 2562306a36Sopenharmony_ci#include <linux/signal.h> 2662306a36Sopenharmony_ci#include <linux/errno.h> 2762306a36Sopenharmony_ci#include <linux/wait.h> 2862306a36Sopenharmony_ci#include <linux/ptrace.h> 2962306a36Sopenharmony_ci#include <linux/unistd.h> 3062306a36Sopenharmony_ci#include <linux/stddef.h> 3162306a36Sopenharmony_ci#include <linux/personality.h> 3262306a36Sopenharmony_ci#include <linux/percpu.h> 3362306a36Sopenharmony_ci#include <linux/linkage.h> 3462306a36Sopenharmony_ci#include <linux/resume_user_mode.h> 3562306a36Sopenharmony_ci#include <asm/entry.h> 3662306a36Sopenharmony_ci#include <asm/ucontext.h> 3762306a36Sopenharmony_ci#include <linux/uaccess.h> 3862306a36Sopenharmony_ci#include <linux/syscalls.h> 3962306a36Sopenharmony_ci#include <asm/cacheflush.h> 4062306a36Sopenharmony_ci#include <asm/syscalls.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Do a signal return; undo the signal stack. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistruct sigframe { 4662306a36Sopenharmony_ci struct sigcontext sc; 4762306a36Sopenharmony_ci unsigned long extramask[_NSIG_WORDS-1]; 4862306a36Sopenharmony_ci unsigned long tramp[2]; /* signal trampoline */ 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct rt_sigframe { 5262306a36Sopenharmony_ci struct siginfo info; 5362306a36Sopenharmony_ci struct ucontext uc; 5462306a36Sopenharmony_ci unsigned long tramp[2]; /* signal trampoline */ 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int restore_sigcontext(struct pt_regs *regs, 5862306a36Sopenharmony_ci struct sigcontext __user *sc, int *rval_p) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci unsigned int err = 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define COPY(x) {err |= __get_user(regs->x, &sc->regs.x); } 6362306a36Sopenharmony_ci COPY(r0); 6462306a36Sopenharmony_ci COPY(r1); 6562306a36Sopenharmony_ci COPY(r2); COPY(r3); COPY(r4); COPY(r5); 6662306a36Sopenharmony_ci COPY(r6); COPY(r7); COPY(r8); COPY(r9); 6762306a36Sopenharmony_ci COPY(r10); COPY(r11); COPY(r12); COPY(r13); 6862306a36Sopenharmony_ci COPY(r14); COPY(r15); COPY(r16); COPY(r17); 6962306a36Sopenharmony_ci COPY(r18); COPY(r19); COPY(r20); COPY(r21); 7062306a36Sopenharmony_ci COPY(r22); COPY(r23); COPY(r24); COPY(r25); 7162306a36Sopenharmony_ci COPY(r26); COPY(r27); COPY(r28); COPY(r29); 7262306a36Sopenharmony_ci COPY(r30); COPY(r31); 7362306a36Sopenharmony_ci COPY(pc); COPY(ear); COPY(esr); COPY(fsr); 7462306a36Sopenharmony_ci#undef COPY 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci *rval_p = regs->r3; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return err; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ciasmlinkage long sys_rt_sigreturn(struct pt_regs *regs) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct rt_sigframe __user *frame = 8462306a36Sopenharmony_ci (struct rt_sigframe __user *)(regs->r1); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci sigset_t set; 8762306a36Sopenharmony_ci int rval; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Always make any pending restarted system calls return -EINTR */ 9062306a36Sopenharmony_ci current->restart_block.fn = do_no_restart_syscall; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!access_ok(frame, sizeof(*frame))) 9362306a36Sopenharmony_ci goto badframe; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) 9662306a36Sopenharmony_ci goto badframe; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci set_current_blocked(&set); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &rval)) 10162306a36Sopenharmony_ci goto badframe; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (restore_altstack(&frame->uc.uc_stack)) 10462306a36Sopenharmony_ci goto badframe; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return rval; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cibadframe: 10962306a36Sopenharmony_ci force_sig(SIGSEGV); 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* 11462306a36Sopenharmony_ci * Set up a signal frame. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int 11862306a36Sopenharmony_cisetup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, 11962306a36Sopenharmony_ci unsigned long mask) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci int err = 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define COPY(x) {err |= __put_user(regs->x, &sc->regs.x); } 12462306a36Sopenharmony_ci COPY(r0); 12562306a36Sopenharmony_ci COPY(r1); 12662306a36Sopenharmony_ci COPY(r2); COPY(r3); COPY(r4); COPY(r5); 12762306a36Sopenharmony_ci COPY(r6); COPY(r7); COPY(r8); COPY(r9); 12862306a36Sopenharmony_ci COPY(r10); COPY(r11); COPY(r12); COPY(r13); 12962306a36Sopenharmony_ci COPY(r14); COPY(r15); COPY(r16); COPY(r17); 13062306a36Sopenharmony_ci COPY(r18); COPY(r19); COPY(r20); COPY(r21); 13162306a36Sopenharmony_ci COPY(r22); COPY(r23); COPY(r24); COPY(r25); 13262306a36Sopenharmony_ci COPY(r26); COPY(r27); COPY(r28); COPY(r29); 13362306a36Sopenharmony_ci COPY(r30); COPY(r31); 13462306a36Sopenharmony_ci COPY(pc); COPY(ear); COPY(esr); COPY(fsr); 13562306a36Sopenharmony_ci#undef COPY 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci err |= __put_user(mask, &sc->oldmask); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return err; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * Determine which stack to use.. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_cistatic inline void __user * 14662306a36Sopenharmony_ciget_sigframe(struct ksignal *ksig, struct pt_regs *regs, size_t frame_size) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci /* Default to using normal stack */ 14962306a36Sopenharmony_ci unsigned long sp = sigsp(regs->r1, ksig); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return (void __user *)((sp - frame_size) & -8UL); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int setup_rt_frame(struct ksignal *ksig, sigset_t *set, 15562306a36Sopenharmony_ci struct pt_regs *regs) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct rt_sigframe __user *frame; 15862306a36Sopenharmony_ci int err = 0, sig = ksig->sig; 15962306a36Sopenharmony_ci unsigned long address = 0; 16062306a36Sopenharmony_ci pmd_t *pmdp; 16162306a36Sopenharmony_ci pte_t *ptep; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci frame = get_sigframe(ksig, regs, sizeof(*frame)); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!access_ok(frame, sizeof(*frame))) 16662306a36Sopenharmony_ci return -EFAULT; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (ksig->ka.sa.sa_flags & SA_SIGINFO) 16962306a36Sopenharmony_ci err |= copy_siginfo_to_user(&frame->info, &ksig->info); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Create the ucontext. */ 17262306a36Sopenharmony_ci err |= __put_user(0, &frame->uc.uc_flags); 17362306a36Sopenharmony_ci err |= __put_user(NULL, &frame->uc.uc_link); 17462306a36Sopenharmony_ci err |= __save_altstack(&frame->uc.uc_stack, regs->r1); 17562306a36Sopenharmony_ci err |= setup_sigcontext(&frame->uc.uc_mcontext, 17662306a36Sopenharmony_ci regs, set->sig[0]); 17762306a36Sopenharmony_ci err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Set up to return from userspace. If provided, use a stub 18062306a36Sopenharmony_ci already in userspace. */ 18162306a36Sopenharmony_ci /* minus 8 is offset to cater for "rtsd r15,8" */ 18262306a36Sopenharmony_ci /* addi r12, r0, __NR_sigreturn */ 18362306a36Sopenharmony_ci err |= __put_user(0x31800000 | __NR_rt_sigreturn , 18462306a36Sopenharmony_ci frame->tramp + 0); 18562306a36Sopenharmony_ci /* brki r14, 0x8 */ 18662306a36Sopenharmony_ci err |= __put_user(0xb9cc0008, frame->tramp + 1); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Return from sighandler will jump to the tramp. 18962306a36Sopenharmony_ci Negative 8 offset because return is rtsd r15, 8 */ 19062306a36Sopenharmony_ci regs->r15 = ((unsigned long)frame->tramp)-8; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci address = ((unsigned long)frame->tramp); 19362306a36Sopenharmony_ci pmdp = pmd_off(current->mm, address); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci preempt_disable(); 19662306a36Sopenharmony_ci ptep = pte_offset_map(pmdp, address); 19762306a36Sopenharmony_ci if (ptep && pte_present(*ptep)) { 19862306a36Sopenharmony_ci address = (unsigned long) page_address(pte_page(*ptep)); 19962306a36Sopenharmony_ci /* MS: I need add offset in page */ 20062306a36Sopenharmony_ci address += ((unsigned long)frame->tramp) & ~PAGE_MASK; 20162306a36Sopenharmony_ci /* MS address is virtual */ 20262306a36Sopenharmony_ci address = __virt_to_phys(address); 20362306a36Sopenharmony_ci invalidate_icache_range(address, address + 8); 20462306a36Sopenharmony_ci flush_dcache_range(address, address + 8); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci if (ptep) 20762306a36Sopenharmony_ci pte_unmap(ptep); 20862306a36Sopenharmony_ci preempt_enable(); 20962306a36Sopenharmony_ci if (err) 21062306a36Sopenharmony_ci return -EFAULT; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Set up registers for signal handler */ 21362306a36Sopenharmony_ci regs->r1 = (unsigned long) frame; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Signal handler args: */ 21662306a36Sopenharmony_ci regs->r5 = sig; /* arg 0: signum */ 21762306a36Sopenharmony_ci regs->r6 = (unsigned long) &frame->info; /* arg 1: siginfo */ 21862306a36Sopenharmony_ci regs->r7 = (unsigned long) &frame->uc; /* arg2: ucontext */ 21962306a36Sopenharmony_ci /* Offset to handle microblaze rtid r14, 0 */ 22062306a36Sopenharmony_ci regs->pc = (unsigned long)ksig->ka.sa.sa_handler; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci#ifdef DEBUG_SIG 22362306a36Sopenharmony_ci pr_info("SIG deliver (%s:%d): sp=%p pc=%08lx\n", 22462306a36Sopenharmony_ci current->comm, current->pid, frame, regs->pc); 22562306a36Sopenharmony_ci#endif 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* Handle restarting system calls */ 23162306a36Sopenharmony_cistatic inline void 23262306a36Sopenharmony_cihandle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci switch (regs->r3) { 23562306a36Sopenharmony_ci case -ERESTART_RESTARTBLOCK: 23662306a36Sopenharmony_ci case -ERESTARTNOHAND: 23762306a36Sopenharmony_ci if (!has_handler) 23862306a36Sopenharmony_ci goto do_restart; 23962306a36Sopenharmony_ci regs->r3 = -EINTR; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci case -ERESTARTSYS: 24262306a36Sopenharmony_ci if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { 24362306a36Sopenharmony_ci regs->r3 = -EINTR; 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci fallthrough; 24762306a36Sopenharmony_ci case -ERESTARTNOINTR: 24862306a36Sopenharmony_cido_restart: 24962306a36Sopenharmony_ci /* offset of 4 bytes to re-execute trap (brki) instruction */ 25062306a36Sopenharmony_ci regs->pc -= 4; 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* 25662306a36Sopenharmony_ci * OK, we're invoking a handler 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic void 26062306a36Sopenharmony_cihandle_signal(struct ksignal *ksig, struct pt_regs *regs) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci sigset_t *oldset = sigmask_to_save(); 26362306a36Sopenharmony_ci int ret; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* Set up the stack frame */ 26662306a36Sopenharmony_ci ret = setup_rt_frame(ksig, oldset, regs); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLESTEP)); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * Note that 'init' is a special process: it doesn't get signals it doesn't 27362306a36Sopenharmony_ci * want to handle. Thus you cannot kill init even with a SIGKILL even by 27462306a36Sopenharmony_ci * mistake. 27562306a36Sopenharmony_ci * 27662306a36Sopenharmony_ci * Note that we go through the signals twice: once to check the signals that 27762306a36Sopenharmony_ci * the kernel can handle, and then we build all the user-level signal handling 27862306a36Sopenharmony_ci * stack-frames in one go after that. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_cistatic void do_signal(struct pt_regs *regs, int in_syscall) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct ksignal ksig; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci#ifdef DEBUG_SIG 28562306a36Sopenharmony_ci pr_info("do signal: %p %d\n", regs, in_syscall); 28662306a36Sopenharmony_ci pr_info("do signal2: %lx %lx %ld [%lx]\n", regs->pc, regs->r1, 28762306a36Sopenharmony_ci regs->r12, read_thread_flags()); 28862306a36Sopenharmony_ci#endif 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (get_signal(&ksig)) { 29162306a36Sopenharmony_ci /* Whee! Actually deliver the signal. */ 29262306a36Sopenharmony_ci if (in_syscall) 29362306a36Sopenharmony_ci handle_restart(regs, &ksig.ka, 1); 29462306a36Sopenharmony_ci handle_signal(&ksig, regs); 29562306a36Sopenharmony_ci return; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (in_syscall) 29962306a36Sopenharmony_ci handle_restart(regs, NULL, 0); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * If there's no signal to deliver, we just put the saved sigmask 30362306a36Sopenharmony_ci * back. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci restore_saved_sigmask(); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ciasmlinkage void do_notify_resume(struct pt_regs *regs, int in_syscall) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci if (test_thread_flag(TIF_SIGPENDING) || 31162306a36Sopenharmony_ci test_thread_flag(TIF_NOTIFY_SIGNAL)) 31262306a36Sopenharmony_ci do_signal(regs, in_syscall); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (test_thread_flag(TIF_NOTIFY_RESUME)) 31562306a36Sopenharmony_ci resume_user_mode_work(regs); 31662306a36Sopenharmony_ci} 317