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