xref: /linux/arch/parisc/kernel/signal.c (revision 3cf5d076fb4d48979f382bc9452765bf8b79e740)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/arch/parisc/kernel/signal.c: Architecture-specific signal
41da177e4SLinus Torvalds  *  handling support.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *  Copyright (C) 2000 David Huggins-Daines <dhd@debian.org>
71da177e4SLinus Torvalds  *  Copyright (C) 2000 Linuxcare, Inc.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  Based on the ia64, i386, and alpha versions.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *  Like the IA-64, we are a recent enough port (we are *starting*
121da177e4SLinus Torvalds  *  with glibc2.2) that we do not need to support the old non-realtime
13c78c2b7eSHelge Deller  *  Linux signals.  Therefore we don't.
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/sched.h>
17b17b0153SIngo Molnar #include <linux/sched/debug.h>
181da177e4SLinus Torvalds #include <linux/mm.h>
191da177e4SLinus Torvalds #include <linux/smp.h>
201da177e4SLinus Torvalds #include <linux/kernel.h>
211da177e4SLinus Torvalds #include <linux/signal.h>
221da177e4SLinus Torvalds #include <linux/errno.h>
231da177e4SLinus Torvalds #include <linux/wait.h>
241da177e4SLinus Torvalds #include <linux/ptrace.h>
25ecf02de5SKyle McMartin #include <linux/tracehook.h>
261da177e4SLinus Torvalds #include <linux/unistd.h>
271da177e4SLinus Torvalds #include <linux/stddef.h>
281da177e4SLinus Torvalds #include <linux/compat.h>
291da177e4SLinus Torvalds #include <linux/elf.h>
301da177e4SLinus Torvalds #include <asm/ucontext.h>
311da177e4SLinus Torvalds #include <asm/rt_sigframe.h>
327c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
331da177e4SLinus Torvalds #include <asm/pgalloc.h>
341da177e4SLinus Torvalds #include <asm/cacheflush.h>
350013a854SSam Ravnborg #include <asm/asm-offsets.h>
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds #ifdef CONFIG_COMPAT
381da177e4SLinus Torvalds #include "signal32.h"
391da177e4SLinus Torvalds #endif
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds #define DEBUG_SIG 0
421da177e4SLinus Torvalds #define DEBUG_SIG_LEVEL 2
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds #if DEBUG_SIG
451da177e4SLinus Torvalds #define DBG(LEVEL, ...) \
461da177e4SLinus Torvalds         ((DEBUG_SIG_LEVEL >= LEVEL) \
471da177e4SLinus Torvalds 	? printk(__VA_ARGS__) : (void) 0)
481da177e4SLinus Torvalds #else
491da177e4SLinus Torvalds #define DBG(LEVEL, ...)
501da177e4SLinus Torvalds #endif
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds /* gcc will complain if a pointer is cast to an integer of different
531da177e4SLinus Torvalds  * size.  If you really need to do this (and we do for an ELF32 user
541da177e4SLinus Torvalds  * application in an ELF64 kernel) then you have to do a cast to an
551da177e4SLinus Torvalds  * integer of the same size first.  The A() macro accomplishes
561da177e4SLinus Torvalds  * this. */
571da177e4SLinus Torvalds #define A(__x)	((unsigned long)(__x))
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds /*
601da177e4SLinus Torvalds  * Do a signal return - restore sigcontext.
611da177e4SLinus Torvalds  */
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds /* Trampoline for calling rt_sigreturn() */
641da177e4SLinus Torvalds #define INSN_LDI_R25_0	 0x34190000 /* ldi  0,%r25 (in_syscall=0) */
651da177e4SLinus Torvalds #define INSN_LDI_R25_1	 0x34190002 /* ldi  1,%r25 (in_syscall=1) */
661da177e4SLinus Torvalds #define INSN_LDI_R20	 0x3414015a /* ldi  __NR_rt_sigreturn,%r20 */
671da177e4SLinus Torvalds #define INSN_BLE_SR2_R0  0xe4008200 /* be,l 0x100(%sr2,%r0),%sr0,%r31 */
681da177e4SLinus Torvalds /* For debugging */
691da177e4SLinus Torvalds #define INSN_DIE_HORRIBLY 0x68000ccc /* stw %r0,0x666(%sr0,%r0) */
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds static long
721da177e4SLinus Torvalds restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds 	long err = 0;
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds 	err |= __copy_from_user(regs->gr, sc->sc_gr, sizeof(regs->gr));
771da177e4SLinus Torvalds 	err |= __copy_from_user(regs->fr, sc->sc_fr, sizeof(regs->fr));
781da177e4SLinus Torvalds 	err |= __copy_from_user(regs->iaoq, sc->sc_iaoq, sizeof(regs->iaoq));
791da177e4SLinus Torvalds 	err |= __copy_from_user(regs->iasq, sc->sc_iasq, sizeof(regs->iasq));
801da177e4SLinus Torvalds 	err |= __get_user(regs->sar, &sc->sc_sar);
818e33a52fSJoe Perches 	DBG(2,"restore_sigcontext: iaoq is %#lx / %#lx\n",
821da177e4SLinus Torvalds 			regs->iaoq[0],regs->iaoq[1]);
831da177e4SLinus Torvalds 	DBG(2,"restore_sigcontext: r28 is %ld\n", regs->gr[28]);
841da177e4SLinus Torvalds 	return err;
851da177e4SLinus Torvalds }
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds void
881da177e4SLinus Torvalds sys_rt_sigreturn(struct pt_regs *regs, int in_syscall)
891da177e4SLinus Torvalds {
901da177e4SLinus Torvalds 	struct rt_sigframe __user *frame;
911da177e4SLinus Torvalds 	sigset_t set;
921da177e4SLinus Torvalds 	unsigned long usp = (regs->gr[30] & ~(0x01UL));
931da177e4SLinus Torvalds 	unsigned long sigframe_size = PARISC_RT_SIGFRAME_SIZE;
94a8f44e38SHelge Deller #ifdef CONFIG_64BIT
951da177e4SLinus Torvalds 	struct compat_rt_sigframe __user * compat_frame;
961da177e4SLinus Torvalds 
97a3ea84faSKyle McMartin 	if (is_compat_task())
981da177e4SLinus Torvalds 		sigframe_size = PARISC_RT_SIGFRAME_SIZE32;
991da177e4SLinus Torvalds #endif
1001da177e4SLinus Torvalds 
101f56141e3SAndy Lutomirski 	current->restart_block.fn = do_no_restart_syscall;
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 	/* Unwind the user stack to get the rt_sigframe structure. */
1041da177e4SLinus Torvalds 	frame = (struct rt_sigframe __user *)
1051da177e4SLinus Torvalds 		(usp - sigframe_size);
1061da177e4SLinus Torvalds 	DBG(2,"sys_rt_sigreturn: frame is %p\n", frame);
1071da177e4SLinus Torvalds 
10800df111eSAl Viro 	regs->orig_r28 = 1; /* no restarts for sigreturn */
10900df111eSAl Viro 
110a8f44e38SHelge Deller #ifdef CONFIG_64BIT
1111da177e4SLinus Torvalds 	compat_frame = (struct compat_rt_sigframe __user *)frame;
1121da177e4SLinus Torvalds 
113a3ea84faSKyle McMartin 	if (is_compat_task()) {
1141da177e4SLinus Torvalds 		DBG(2,"sys_rt_sigreturn: ELF32 process.\n");
115d74f0f47SAl Viro 		if (get_compat_sigset(&set, &compat_frame->uc.uc_sigmask))
1161da177e4SLinus Torvalds 			goto give_sigsegv;
1171da177e4SLinus Torvalds 	} else
1181da177e4SLinus Torvalds #endif
1191da177e4SLinus Torvalds 	{
1201da177e4SLinus Torvalds 		if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
1211da177e4SLinus Torvalds 			goto give_sigsegv;
1221da177e4SLinus Torvalds 	}
1231da177e4SLinus Torvalds 
124ade7728bSMatt Fleming 	set_current_blocked(&set);
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	/* Good thing we saved the old gr[30], eh? */
127a8f44e38SHelge Deller #ifdef CONFIG_64BIT
128a3ea84faSKyle McMartin 	if (is_compat_task()) {
1291da177e4SLinus Torvalds 		DBG(1,"sys_rt_sigreturn: compat_frame->uc.uc_mcontext 0x%p\n",
1301da177e4SLinus Torvalds 				&compat_frame->uc.uc_mcontext);
1311da177e4SLinus Torvalds // FIXME: Load upper half from register file
1321da177e4SLinus Torvalds 		if (restore_sigcontext32(&compat_frame->uc.uc_mcontext,
1331da177e4SLinus Torvalds 					&compat_frame->regs, regs))
1341da177e4SLinus Torvalds 			goto give_sigsegv;
1351da177e4SLinus Torvalds 		DBG(1,"sys_rt_sigreturn: usp %#08lx stack 0x%p\n",
1361da177e4SLinus Torvalds 				usp, &compat_frame->uc.uc_stack);
1376e26aab9SAl Viro 		if (compat_restore_altstack(&compat_frame->uc.uc_stack))
1381da177e4SLinus Torvalds 			goto give_sigsegv;
1391da177e4SLinus Torvalds 	} else
1401da177e4SLinus Torvalds #endif
1411da177e4SLinus Torvalds 	{
1421da177e4SLinus Torvalds 		DBG(1,"sys_rt_sigreturn: frame->uc.uc_mcontext 0x%p\n",
1431da177e4SLinus Torvalds 				&frame->uc.uc_mcontext);
1441da177e4SLinus Torvalds 		if (restore_sigcontext(&frame->uc.uc_mcontext, regs))
1451da177e4SLinus Torvalds 			goto give_sigsegv;
1461da177e4SLinus Torvalds 		DBG(1,"sys_rt_sigreturn: usp %#08lx stack 0x%p\n",
1471da177e4SLinus Torvalds 				usp, &frame->uc.uc_stack);
1486e26aab9SAl Viro 		if (restore_altstack(&frame->uc.uc_stack))
1491da177e4SLinus Torvalds 			goto give_sigsegv;
1501da177e4SLinus Torvalds 	}
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 	/* If we are on the syscall path IAOQ will not be restored, and
1551da177e4SLinus Torvalds 	 * if we are on the interrupt path we must not corrupt gr31.
1561da177e4SLinus Torvalds 	 */
1571da177e4SLinus Torvalds 	if (in_syscall)
1581da177e4SLinus Torvalds 		regs->gr[31] = regs->iaoq[0];
1591da177e4SLinus Torvalds #if DEBUG_SIG
1601da177e4SLinus Torvalds 	DBG(1,"sys_rt_sigreturn: returning to %#lx, DUMPING REGS:\n", regs->iaoq[0]);
1611da177e4SLinus Torvalds 	show_regs(regs);
1621da177e4SLinus Torvalds #endif
1631da177e4SLinus Torvalds 	return;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds give_sigsegv:
1661da177e4SLinus Torvalds 	DBG(1,"sys_rt_sigreturn: Sending SIGSEGV\n");
167*3cf5d076SEric W. Biederman 	force_sig(SIGSEGV);
1681da177e4SLinus Torvalds 	return;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds /*
1721da177e4SLinus Torvalds  * Set up a signal frame.
1731da177e4SLinus Torvalds  */
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds static inline void __user *
1761da177e4SLinus Torvalds get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
1771da177e4SLinus Torvalds {
1781da177e4SLinus Torvalds 	/*FIXME: ELF32 vs. ELF64 has different frame_size, but since we
1791da177e4SLinus Torvalds 	  don't use the parameter it doesn't matter */
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 	DBG(1,"get_sigframe: ka = %#lx, sp = %#lx, frame_size = %#lx\n",
1821da177e4SLinus Torvalds 			(unsigned long)ka, sp, frame_size);
1831da177e4SLinus Torvalds 
184ad30f3ffSJohn David Anglin 	/* Align alternate stack and reserve 64 bytes for the signal
185ad30f3ffSJohn David Anglin 	   handler's frame marker.  */
186d09042daSLaurent MEYER 	if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! sas_ss_flags(sp))
187ad30f3ffSJohn David Anglin 		sp = (current->sas_ss_sp + 0x7f) & ~0x3f; /* Stacks grow up! */
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 	DBG(1,"get_sigframe: Returning sp = %#lx\n", (unsigned long)sp);
1901da177e4SLinus Torvalds 	return (void __user *) sp; /* Stacks grow up.  Fun. */
1911da177e4SLinus Torvalds }
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds static long
1941da177e4SLinus Torvalds setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, int in_syscall)
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds 	unsigned long flags = 0;
1981da177e4SLinus Torvalds 	long err = 0;
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	if (on_sig_stack((unsigned long) sc))
2011da177e4SLinus Torvalds 		flags |= PARISC_SC_FLAG_ONSTACK;
2021da177e4SLinus Torvalds 	if (in_syscall) {
2031da177e4SLinus Torvalds 		flags |= PARISC_SC_FLAG_IN_SYSCALL;
2041da177e4SLinus Torvalds 		/* regs->iaoq is undefined in the syscall return path */
2051da177e4SLinus Torvalds 		err |= __put_user(regs->gr[31], &sc->sc_iaoq[0]);
2061da177e4SLinus Torvalds 		err |= __put_user(regs->gr[31]+4, &sc->sc_iaoq[1]);
2071da177e4SLinus Torvalds 		err |= __put_user(regs->sr[3], &sc->sc_iasq[0]);
2081da177e4SLinus Torvalds 		err |= __put_user(regs->sr[3], &sc->sc_iasq[1]);
2091da177e4SLinus Torvalds 		DBG(1,"setup_sigcontext: iaoq %#lx / %#lx (in syscall)\n",
2101da177e4SLinus Torvalds 			regs->gr[31], regs->gr[31]+4);
2111da177e4SLinus Torvalds 	} else {
2121da177e4SLinus Torvalds 		err |= __copy_to_user(sc->sc_iaoq, regs->iaoq, sizeof(regs->iaoq));
2131da177e4SLinus Torvalds 		err |= __copy_to_user(sc->sc_iasq, regs->iasq, sizeof(regs->iasq));
2141da177e4SLinus Torvalds 		DBG(1,"setup_sigcontext: iaoq %#lx / %#lx (not in syscall)\n",
2151da177e4SLinus Torvalds 			regs->iaoq[0], regs->iaoq[1]);
2161da177e4SLinus Torvalds 	}
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds 	err |= __put_user(flags, &sc->sc_flags);
2191da177e4SLinus Torvalds 	err |= __copy_to_user(sc->sc_gr, regs->gr, sizeof(regs->gr));
2201da177e4SLinus Torvalds 	err |= __copy_to_user(sc->sc_fr, regs->fr, sizeof(regs->fr));
2211da177e4SLinus Torvalds 	err |= __put_user(regs->sar, &sc->sc_sar);
2221da177e4SLinus Torvalds 	DBG(1,"setup_sigcontext: r28 is %ld\n", regs->gr[28]);
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds 	return err;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds static long
228e4dc894bSRichard Weinberger setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs,
229e4dc894bSRichard Weinberger 	       int in_syscall)
2301da177e4SLinus Torvalds {
2311da177e4SLinus Torvalds 	struct rt_sigframe __user *frame;
2321da177e4SLinus Torvalds 	unsigned long rp, usp;
2331da177e4SLinus Torvalds 	unsigned long haddr, sigframe_size;
234ef470a60SJohn David Anglin 	unsigned long start, end;
2351da177e4SLinus Torvalds 	int err = 0;
236a8f44e38SHelge Deller #ifdef CONFIG_64BIT
2371da177e4SLinus Torvalds 	struct compat_rt_sigframe __user * compat_frame;
2381da177e4SLinus Torvalds #endif
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	usp = (regs->gr[30] & ~(0x01UL));
2411da177e4SLinus Torvalds 	/*FIXME: frame_size parameter is unused, remove it. */
242e4dc894bSRichard Weinberger 	frame = get_sigframe(&ksig->ka, usp, sizeof(*frame));
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	DBG(1,"SETUP_RT_FRAME: START\n");
245e4dc894bSRichard Weinberger 	DBG(1,"setup_rt_frame: frame %p info %p\n", frame, ksig->info);
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds 
248a8f44e38SHelge Deller #ifdef CONFIG_64BIT
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	compat_frame = (struct compat_rt_sigframe __user *)frame;
2511da177e4SLinus Torvalds 
252a3ea84faSKyle McMartin 	if (is_compat_task()) {
2531da177e4SLinus Torvalds 		DBG(1,"setup_rt_frame: frame->info = 0x%p\n", &compat_frame->info);
254e4dc894bSRichard Weinberger 		err |= copy_siginfo_to_user32(&compat_frame->info, &ksig->info);
2556e26aab9SAl Viro 		err |= __compat_save_altstack( &compat_frame->uc.uc_stack, regs->gr[30]);
2561da177e4SLinus Torvalds 		DBG(1,"setup_rt_frame: frame->uc = 0x%p\n", &compat_frame->uc);
2571da177e4SLinus Torvalds 		DBG(1,"setup_rt_frame: frame->uc.uc_mcontext = 0x%p\n", &compat_frame->uc.uc_mcontext);
2581da177e4SLinus Torvalds 		err |= setup_sigcontext32(&compat_frame->uc.uc_mcontext,
2591da177e4SLinus Torvalds 					&compat_frame->regs, regs, in_syscall);
260d74f0f47SAl Viro 		err |= put_compat_sigset(&compat_frame->uc.uc_sigmask, set,
261d74f0f47SAl Viro 					 sizeof(compat_sigset_t));
2621da177e4SLinus Torvalds 	} else
2631da177e4SLinus Torvalds #endif
2641da177e4SLinus Torvalds 	{
2651da177e4SLinus Torvalds 		DBG(1,"setup_rt_frame: frame->info = 0x%p\n", &frame->info);
266e4dc894bSRichard Weinberger 		err |= copy_siginfo_to_user(&frame->info, &ksig->info);
2676e26aab9SAl Viro 		err |= __save_altstack(&frame->uc.uc_stack, regs->gr[30]);
2681da177e4SLinus Torvalds 		DBG(1,"setup_rt_frame: frame->uc = 0x%p\n", &frame->uc);
2691da177e4SLinus Torvalds 		DBG(1,"setup_rt_frame: frame->uc.uc_mcontext = 0x%p\n", &frame->uc.uc_mcontext);
2701da177e4SLinus Torvalds 		err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, in_syscall);
2711da177e4SLinus Torvalds 		/* FIXME: Should probably be converted as well for the compat case */
2721da177e4SLinus Torvalds 		err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
2731da177e4SLinus Torvalds 	}
2741da177e4SLinus Torvalds 
2751da177e4SLinus Torvalds 	if (err)
276e4dc894bSRichard Weinberger 		return -EFAULT;
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	/* Set up to return from userspace.  If provided, use a stub
2791da177e4SLinus Torvalds 	   already in userspace. The first words of tramp are used to
2801da177e4SLinus Torvalds 	   save the previous sigrestartblock trampoline that might be
2811da177e4SLinus Torvalds 	   on the stack. We start the sigreturn trampoline at
2821da177e4SLinus Torvalds 	   SIGRESTARTBLOCK_TRAMP+X. */
2831da177e4SLinus Torvalds 	err |= __put_user(in_syscall ? INSN_LDI_R25_1 : INSN_LDI_R25_0,
2841da177e4SLinus Torvalds 			&frame->tramp[SIGRESTARTBLOCK_TRAMP+0]);
2851da177e4SLinus Torvalds 	err |= __put_user(INSN_LDI_R20,
2861da177e4SLinus Torvalds 			&frame->tramp[SIGRESTARTBLOCK_TRAMP+1]);
2871da177e4SLinus Torvalds 	err |= __put_user(INSN_BLE_SR2_R0,
2881da177e4SLinus Torvalds 			&frame->tramp[SIGRESTARTBLOCK_TRAMP+2]);
2891da177e4SLinus Torvalds 	err |= __put_user(INSN_NOP, &frame->tramp[SIGRESTARTBLOCK_TRAMP+3]);
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds #if DEBUG_SIG
2921da177e4SLinus Torvalds 	/* Assert that we're flushing in the correct space... */
2931da177e4SLinus Torvalds 	{
29445b6eff2SJohn David Anglin 		unsigned long sid;
2951da177e4SLinus Torvalds 		asm ("mfsp %%sr3,%0" : "=r" (sid));
2961da177e4SLinus Torvalds 		DBG(1,"setup_rt_frame: Flushing 64 bytes at space %#x offset %p\n",
2971da177e4SLinus Torvalds 		       sid, frame->tramp);
2981da177e4SLinus Torvalds 	}
2991da177e4SLinus Torvalds #endif
3001da177e4SLinus Torvalds 
301ef470a60SJohn David Anglin 	start = (unsigned long) &frame->tramp[0];
302ef470a60SJohn David Anglin 	end = (unsigned long) &frame->tramp[TRAMP_SIZE];
303ef470a60SJohn David Anglin 	flush_user_dcache_range_asm(start, end);
304ef470a60SJohn David Anglin 	flush_user_icache_range_asm(start, end);
3051da177e4SLinus Torvalds 
306efad798bSPaulius Zaleckas 	/* TRAMP Words 0-4, Length 5 = SIGRESTARTBLOCK_TRAMP
3071da177e4SLinus Torvalds 	 * TRAMP Words 5-9, Length 4 = SIGRETURN_TRAMP
3081da177e4SLinus Torvalds 	 * So the SIGRETURN_TRAMP is at the end of SIGRESTARTBLOCK_TRAMP
3091da177e4SLinus Torvalds 	 */
3101da177e4SLinus Torvalds 	rp = (unsigned long) &frame->tramp[SIGRESTARTBLOCK_TRAMP];
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	if (err)
313e4dc894bSRichard Weinberger 		return -EFAULT;
3141da177e4SLinus Torvalds 
315e4dc894bSRichard Weinberger 	haddr = A(ksig->ka.sa.sa_handler);
3161da177e4SLinus Torvalds 	/* The sa_handler may be a pointer to a function descriptor */
317a8f44e38SHelge Deller #ifdef CONFIG_64BIT
318a3ea84faSKyle McMartin 	if (is_compat_task()) {
3191da177e4SLinus Torvalds #endif
3201da177e4SLinus Torvalds 		if (haddr & PA_PLABEL_FDESC) {
3211da177e4SLinus Torvalds 			Elf32_Fdesc fdesc;
3221da177e4SLinus Torvalds 			Elf32_Fdesc __user *ufdesc = (Elf32_Fdesc __user *)A(haddr & ~3);
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 			err = __copy_from_user(&fdesc, ufdesc, sizeof(fdesc));
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds 			if (err)
327e4dc894bSRichard Weinberger 				return -EFAULT;
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 			haddr = fdesc.addr;
3301da177e4SLinus Torvalds 			regs->gr[19] = fdesc.gp;
3311da177e4SLinus Torvalds 		}
332a8f44e38SHelge Deller #ifdef CONFIG_64BIT
3331da177e4SLinus Torvalds 	} else {
3341da177e4SLinus Torvalds 		Elf64_Fdesc fdesc;
3351da177e4SLinus Torvalds 		Elf64_Fdesc __user *ufdesc = (Elf64_Fdesc __user *)A(haddr & ~3);
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds 		err = __copy_from_user(&fdesc, ufdesc, sizeof(fdesc));
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds 		if (err)
340e4dc894bSRichard Weinberger 			return -EFAULT;
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 		haddr = fdesc.addr;
3431da177e4SLinus Torvalds 		regs->gr[19] = fdesc.gp;
3441da177e4SLinus Torvalds 		DBG(1,"setup_rt_frame: 64 bit signal, exe=%#lx, r19=%#lx, in_syscall=%d\n",
3451da177e4SLinus Torvalds 		     haddr, regs->gr[19], in_syscall);
3461da177e4SLinus Torvalds 	}
3471da177e4SLinus Torvalds #endif
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds 	/* The syscall return path will create IAOQ values from r31.
3501da177e4SLinus Torvalds 	 */
3511da177e4SLinus Torvalds 	sigframe_size = PARISC_RT_SIGFRAME_SIZE;
352a8f44e38SHelge Deller #ifdef CONFIG_64BIT
353a3ea84faSKyle McMartin 	if (is_compat_task())
3541da177e4SLinus Torvalds 		sigframe_size = PARISC_RT_SIGFRAME_SIZE32;
3551da177e4SLinus Torvalds #endif
3561da177e4SLinus Torvalds 	if (in_syscall) {
3571da177e4SLinus Torvalds 		regs->gr[31] = haddr;
358a8f44e38SHelge Deller #ifdef CONFIG_64BIT
359df570b9cSKyle McMartin 		if (!test_thread_flag(TIF_32BIT))
3601da177e4SLinus Torvalds 			sigframe_size |= 1;
3611da177e4SLinus Torvalds #endif
3621da177e4SLinus Torvalds 	} else {
3631da177e4SLinus Torvalds 		unsigned long psw = USER_PSW;
364a8f44e38SHelge Deller #ifdef CONFIG_64BIT
365df570b9cSKyle McMartin 		if (!test_thread_flag(TIF_32BIT))
3661da177e4SLinus Torvalds 			psw |= PSW_W;
3671da177e4SLinus Torvalds #endif
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds 		/* If we are singlestepping, arrange a trap to be delivered
3701da177e4SLinus Torvalds 		   when we return to userspace. Note the semantics -- we
3711da177e4SLinus Torvalds 		   should trap before the first insn in the handler is
3721da177e4SLinus Torvalds 		   executed. Ref:
3731da177e4SLinus Torvalds 			http://sources.redhat.com/ml/gdb/2004-11/msg00245.html
3741da177e4SLinus Torvalds 		 */
3751da177e4SLinus Torvalds 		if (pa_psw(current)->r) {
3761da177e4SLinus Torvalds 			pa_psw(current)->r = 0;
3771da177e4SLinus Torvalds 			psw |= PSW_R;
3781da177e4SLinus Torvalds 			mtctl(-1, 0);
3791da177e4SLinus Torvalds 		}
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds 		regs->gr[0] = psw;
3821da177e4SLinus Torvalds 		regs->iaoq[0] = haddr | 3;
3831da177e4SLinus Torvalds 		regs->iaoq[1] = regs->iaoq[0] + 4;
3841da177e4SLinus Torvalds 	}
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 	regs->gr[2]  = rp;                /* userland return pointer */
387e4dc894bSRichard Weinberger 	regs->gr[26] = ksig->sig;               /* signal number */
3881da177e4SLinus Torvalds 
389a8f44e38SHelge Deller #ifdef CONFIG_64BIT
390a3ea84faSKyle McMartin 	if (is_compat_task()) {
3911da177e4SLinus Torvalds 		regs->gr[25] = A(&compat_frame->info); /* siginfo pointer */
3921da177e4SLinus Torvalds 		regs->gr[24] = A(&compat_frame->uc);   /* ucontext pointer */
3931da177e4SLinus Torvalds 	} else
3941da177e4SLinus Torvalds #endif
3951da177e4SLinus Torvalds 	{
3961da177e4SLinus Torvalds 		regs->gr[25] = A(&frame->info); /* siginfo pointer */
3971da177e4SLinus Torvalds 		regs->gr[24] = A(&frame->uc);   /* ucontext pointer */
3981da177e4SLinus Torvalds 	}
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds 	DBG(1,"setup_rt_frame: making sigreturn frame: %#lx + %#lx = %#lx\n",
4011da177e4SLinus Torvalds 	       regs->gr[30], sigframe_size,
4021da177e4SLinus Torvalds 	       regs->gr[30] + sigframe_size);
4031da177e4SLinus Torvalds 	/* Raise the user stack pointer to make a proper call frame. */
4041da177e4SLinus Torvalds 	regs->gr[30] = (A(frame) + sigframe_size);
4051da177e4SLinus Torvalds 
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	DBG(1,"setup_rt_frame: sig deliver (%s,%d) frame=0x%p sp=%#lx iaoq=%#lx/%#lx rp=%#lx\n",
4081da177e4SLinus Torvalds 	       current->comm, current->pid, frame, regs->gr[30],
4091da177e4SLinus Torvalds 	       regs->iaoq[0], regs->iaoq[1], rp);
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 	return 0;
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds /*
4151da177e4SLinus Torvalds  * OK, we're invoking a handler.
4161da177e4SLinus Torvalds  */
4171da177e4SLinus Torvalds 
418e3b880c6SAl Viro static void
419e4dc894bSRichard Weinberger handle_signal(struct ksignal *ksig, struct pt_regs *regs, int in_syscall)
4201da177e4SLinus Torvalds {
421e4dc894bSRichard Weinberger 	int ret;
422b7f9a11aSAl Viro 	sigset_t *oldset = sigmask_to_save();
423e4dc894bSRichard Weinberger 
4241da177e4SLinus Torvalds 	DBG(1,"handle_signal: sig=%ld, ka=%p, info=%p, oldset=%p, regs=%p\n",
425e4dc894bSRichard Weinberger 	       ksig->sig, ksig->ka, ksig->info, oldset, regs);
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	/* Set up the stack frame */
428e4dc894bSRichard Weinberger 	ret = setup_rt_frame(ksig, oldset, regs, in_syscall);
4291da177e4SLinus Torvalds 
430e4dc894bSRichard Weinberger 	signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLESTEP) ||
43122a8cdd6SKyle McMartin 			  test_thread_flag(TIF_BLOCKSTEP));
432ecf02de5SKyle McMartin 
433a610d6e6SAl Viro 	DBG(1,KERN_DEBUG "do_signal: Exit (success), regs->gr[28] = %ld\n",
434a610d6e6SAl Viro 		regs->gr[28]);
4351da177e4SLinus Torvalds }
4361da177e4SLinus Torvalds 
43771a71fb5SHelge Deller /*
43871a71fb5SHelge Deller  * Check how the syscall number gets loaded into %r20 within
43971a71fb5SHelge Deller  * the delay branch in userspace and adjust as needed.
44071a71fb5SHelge Deller  */
44171a71fb5SHelge Deller 
44271a71fb5SHelge Deller static void check_syscallno_in_delay_branch(struct pt_regs *regs)
44371a71fb5SHelge Deller {
44471a71fb5SHelge Deller 	u32 opcode, source_reg;
44571a71fb5SHelge Deller 	u32 __user *uaddr;
44671a71fb5SHelge Deller 	int err;
44771a71fb5SHelge Deller 
44871a71fb5SHelge Deller 	/* Usually we don't have to restore %r20 (the system call number)
44971a71fb5SHelge Deller 	 * because it gets loaded in the delay slot of the branch external
45071a71fb5SHelge Deller 	 * instruction via the ldi instruction.
45171a71fb5SHelge Deller 	 * In some cases a register-to-register copy instruction might have
45271a71fb5SHelge Deller 	 * been used instead, in which case we need to copy the syscall
45371a71fb5SHelge Deller 	 * number into the source register before returning to userspace.
45471a71fb5SHelge Deller 	 */
45571a71fb5SHelge Deller 
45671a71fb5SHelge Deller 	/* A syscall is just a branch, so all we have to do is fiddle the
45771a71fb5SHelge Deller 	 * return pointer so that the ble instruction gets executed again.
45871a71fb5SHelge Deller 	 */
45971a71fb5SHelge Deller 	regs->gr[31] -= 8; /* delayed branching */
46071a71fb5SHelge Deller 
46171a71fb5SHelge Deller 	/* Get assembler opcode of code in delay branch */
46271a71fb5SHelge Deller 	uaddr = (unsigned int *) ((regs->gr[31] & ~3) + 4);
46371a71fb5SHelge Deller 	err = get_user(opcode, uaddr);
46471a71fb5SHelge Deller 	if (err)
46571a71fb5SHelge Deller 		return;
46671a71fb5SHelge Deller 
46771a71fb5SHelge Deller 	/* Check if delay branch uses "ldi int,%r20" */
46871a71fb5SHelge Deller 	if ((opcode & 0xffff0000) == 0x34140000)
46971a71fb5SHelge Deller 		return;	/* everything ok, just return */
47071a71fb5SHelge Deller 
47171a71fb5SHelge Deller 	/* Check if delay branch uses "nop" */
47271a71fb5SHelge Deller 	if (opcode == INSN_NOP)
47371a71fb5SHelge Deller 		return;
47471a71fb5SHelge Deller 
47571a71fb5SHelge Deller 	/* Check if delay branch uses "copy %rX,%r20" */
47671a71fb5SHelge Deller 	if ((opcode & 0xffe0ffff) == 0x08000254) {
47771a71fb5SHelge Deller 		source_reg = (opcode >> 16) & 31;
47871a71fb5SHelge Deller 		regs->gr[source_reg] = regs->gr[20];
47971a71fb5SHelge Deller 		return;
48071a71fb5SHelge Deller 	}
48171a71fb5SHelge Deller 
48271a71fb5SHelge Deller 	pr_warn("syscall restart: %s (pid %d): unexpected opcode 0x%08x\n",
48371a71fb5SHelge Deller 		current->comm, task_pid_nr(current), opcode);
48471a71fb5SHelge Deller }
48571a71fb5SHelge Deller 
4862b163b71SKyle McMartin static inline void
4872b163b71SKyle McMartin syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
4882b163b71SKyle McMartin {
48900df111eSAl Viro 	if (regs->orig_r28)
49000df111eSAl Viro 		return;
49100df111eSAl Viro 	regs->orig_r28 = 1; /* no more restarts */
4922b163b71SKyle McMartin 	/* Check the return code */
4932b163b71SKyle McMartin 	switch (regs->gr[28]) {
4942b163b71SKyle McMartin 	case -ERESTART_RESTARTBLOCK:
4952b163b71SKyle McMartin 	case -ERESTARTNOHAND:
4962b163b71SKyle McMartin 		DBG(1,"ERESTARTNOHAND: returning -EINTR\n");
4972b163b71SKyle McMartin 		regs->gr[28] = -EINTR;
4982b163b71SKyle McMartin 		break;
4992b163b71SKyle McMartin 
5002b163b71SKyle McMartin 	case -ERESTARTSYS:
5012b163b71SKyle McMartin 		if (!(ka->sa.sa_flags & SA_RESTART)) {
5022b163b71SKyle McMartin 			DBG(1,"ERESTARTSYS: putting -EINTR\n");
5032b163b71SKyle McMartin 			regs->gr[28] = -EINTR;
5042b163b71SKyle McMartin 			break;
5052b163b71SKyle McMartin 		}
5062b163b71SKyle McMartin 		/* fallthrough */
5072b163b71SKyle McMartin 	case -ERESTARTNOINTR:
50871a71fb5SHelge Deller 		check_syscallno_in_delay_branch(regs);
5092b163b71SKyle McMartin 		break;
5102b163b71SKyle McMartin 	}
5112b163b71SKyle McMartin }
5122b163b71SKyle McMartin 
5132b163b71SKyle McMartin static inline void
5142b163b71SKyle McMartin insert_restart_trampoline(struct pt_regs *regs)
5152b163b71SKyle McMartin {
51600df111eSAl Viro 	if (regs->orig_r28)
51700df111eSAl Viro 		return;
51800df111eSAl Viro 	regs->orig_r28 = 1; /* no more restarts */
5192b163b71SKyle McMartin 	switch(regs->gr[28]) {
5202b163b71SKyle McMartin 	case -ERESTART_RESTARTBLOCK: {
5212b163b71SKyle McMartin 		/* Restart the system call - no handlers present */
5222b163b71SKyle McMartin 		unsigned int *usp = (unsigned int *)regs->gr[30];
5230bd1e94bSHelge Deller 		unsigned long start = (unsigned long) &usp[2];
5240bd1e94bSHelge Deller 		unsigned long end  = (unsigned long) &usp[5];
5250bd1e94bSHelge Deller 		long err = 0;
5262b163b71SKyle McMartin 
5272b163b71SKyle McMartin 		/* Setup a trampoline to restart the syscall
5282b163b71SKyle McMartin 		 * with __NR_restart_syscall
5292b163b71SKyle McMartin 		 *
5302b163b71SKyle McMartin 		 *  0: <return address (orig r31)>
5312b163b71SKyle McMartin 		 *  4: <2nd half for 64-bit>
5322b163b71SKyle McMartin 		 *  8: ldw 0(%sp), %r31
5332b163b71SKyle McMartin 		 * 12: be 0x100(%sr2, %r0)
5342b163b71SKyle McMartin 		 * 16: ldi __NR_restart_syscall, %r20
5352b163b71SKyle McMartin 		 */
5362b163b71SKyle McMartin #ifdef CONFIG_64BIT
5370bd1e94bSHelge Deller 		err |= put_user(regs->gr[31] >> 32, &usp[0]);
5380bd1e94bSHelge Deller 		err |= put_user(regs->gr[31] & 0xffffffff, &usp[1]);
5390bd1e94bSHelge Deller 		err |= put_user(0x0fc010df, &usp[2]);
5402b163b71SKyle McMartin #else
5410bd1e94bSHelge Deller 		err |= put_user(regs->gr[31], &usp[0]);
5420bd1e94bSHelge Deller 		err |= put_user(0x0fc0109f, &usp[2]);
5432b163b71SKyle McMartin #endif
5440bd1e94bSHelge Deller 		err |= put_user(0xe0008200, &usp[3]);
5450bd1e94bSHelge Deller 		err |= put_user(0x34140000, &usp[4]);
5462b163b71SKyle McMartin 
5470bd1e94bSHelge Deller 		WARN_ON(err);
5480bd1e94bSHelge Deller 
5490bd1e94bSHelge Deller 		/* flush data/instruction cache for new insns */
550ef470a60SJohn David Anglin 		flush_user_dcache_range_asm(start, end);
551ef470a60SJohn David Anglin 		flush_user_icache_range_asm(start, end);
5522b163b71SKyle McMartin 
5532b163b71SKyle McMartin 		regs->gr[31] = regs->gr[30] + 8;
5542b163b71SKyle McMartin 		return;
5552b163b71SKyle McMartin 	}
5562b163b71SKyle McMartin 	case -ERESTARTNOHAND:
5572b163b71SKyle McMartin 	case -ERESTARTSYS:
55871a71fb5SHelge Deller 	case -ERESTARTNOINTR:
55971a71fb5SHelge Deller 		check_syscallno_in_delay_branch(regs);
5602b163b71SKyle McMartin 		return;
5612b163b71SKyle McMartin 	default:
5622b163b71SKyle McMartin 		break;
5632b163b71SKyle McMartin 	}
5642b163b71SKyle McMartin }
5652b163b71SKyle McMartin 
5661da177e4SLinus Torvalds /*
5671da177e4SLinus Torvalds  * Note that 'init' is a special process: it doesn't get signals it doesn't
5681da177e4SLinus Torvalds  * want to handle. Thus you cannot kill init even with a SIGKILL even by
5691da177e4SLinus Torvalds  * mistake.
5701da177e4SLinus Torvalds  *
5711da177e4SLinus Torvalds  * We need to be able to restore the syscall arguments (r21-r26) to
5721da177e4SLinus Torvalds  * restart syscalls.  Thus, the syscall path should save them in the
5731da177e4SLinus Torvalds  * pt_regs structure (it's okay to do so since they are caller-save
5741da177e4SLinus Torvalds  * registers).  As noted below, the syscall number gets restored for
5751da177e4SLinus Torvalds  * us due to the magic of delayed branching.
5761da177e4SLinus Torvalds  */
5774650f0a5SKyle McMartin asmlinkage void
5784650f0a5SKyle McMartin do_signal(struct pt_regs *regs, long in_syscall)
5791da177e4SLinus Torvalds {
580e4dc894bSRichard Weinberger 	struct ksignal ksig;
5811da177e4SLinus Torvalds 
582b7f9a11aSAl Viro 	DBG(1,"\ndo_signal: regs=0x%p, sr7 %#lx, in_syscall=%d\n",
583b7f9a11aSAl Viro 	       regs, regs->sr[7], in_syscall);
5841da177e4SLinus Torvalds 
585e4dc894bSRichard Weinberger 	if (get_signal(&ksig)) {
5861da177e4SLinus Torvalds 		DBG(3,"do_signal: signr = %d, regs->gr[28] = %ld\n", signr, regs->gr[28]);
5871da177e4SLinus Torvalds 		/* Restart a system call if necessary. */
5882b163b71SKyle McMartin 		if (in_syscall)
589e4dc894bSRichard Weinberger 			syscall_restart(regs, &ksig.ka);
5901da177e4SLinus Torvalds 
591e4dc894bSRichard Weinberger 		handle_signal(&ksig, regs, in_syscall);
5924650f0a5SKyle McMartin 		return;
5931da177e4SLinus Torvalds 	}
5941da177e4SLinus Torvalds 
5951da177e4SLinus Torvalds 	/* Did we come from a system call? */
5962b163b71SKyle McMartin 	if (in_syscall)
5972b163b71SKyle McMartin 		insert_restart_trampoline(regs);
5981da177e4SLinus Torvalds 
5991da177e4SLinus Torvalds 	DBG(1,"do_signal: Exit (not delivered), regs->gr[28] = %ld\n",
6001da177e4SLinus Torvalds 		regs->gr[28]);
6011da177e4SLinus Torvalds 
60251a7b448SAl Viro 	restore_saved_sigmask();
6034650f0a5SKyle McMartin }
6044650f0a5SKyle McMartin 
6054650f0a5SKyle McMartin void do_notify_resume(struct pt_regs *regs, long in_syscall)
6064650f0a5SKyle McMartin {
6076fd84c08SAl Viro 	if (test_thread_flag(TIF_SIGPENDING))
6084650f0a5SKyle McMartin 		do_signal(regs, in_syscall);
609d0420c83SDavid Howells 
610d0420c83SDavid Howells 	if (test_thread_flag(TIF_NOTIFY_RESUME)) {
611d0420c83SDavid Howells 		clear_thread_flag(TIF_NOTIFY_RESUME);
612d0420c83SDavid Howells 		tracehook_notify_resume(regs);
613d0420c83SDavid Howells 	}
6141da177e4SLinus Torvalds }
615