Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b163b71 authored by Kyle McMartin's avatar Kyle McMartin Committed by Kyle McMartin
Browse files

[PARISC] factor syscall_restart code out of do_signal



looks better this way... ;)

Signed-off-by: default avatarKyle McMartin <kyle@parisc-linux.org>
parent f354ef8a
Loading
Loading
Loading
Loading
+98 −79
Original line number Diff line number Diff line
@@ -471,6 +471,97 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
	return 1;
}

static inline void
syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
{
	/* Check the return code */
	switch (regs->gr[28]) {
	case -ERESTART_RESTARTBLOCK:
		current_thread_info()->restart_block.fn =
			do_no_restart_syscall;
	case -ERESTARTNOHAND:
		DBG(1,"ERESTARTNOHAND: returning -EINTR\n");
		regs->gr[28] = -EINTR;
		break;

	case -ERESTARTSYS:
		if (!(ka->sa.sa_flags & SA_RESTART)) {
			DBG(1,"ERESTARTSYS: putting -EINTR\n");
			regs->gr[28] = -EINTR;
			break;
		}
		/* fallthrough */
	case -ERESTARTNOINTR:
		/* A syscall is just a branch, so all
		 * we have to do is fiddle the return pointer.
		 */
		regs->gr[31] -= 8; /* delayed branching */
		/* Preserve original r28. */
		regs->gr[28] = regs->orig_r28;
		break;
	}
}

static inline void
insert_restart_trampoline(struct pt_regs *regs)
{
	switch(regs->gr[28]) {
	case -ERESTART_RESTARTBLOCK: {
		/* Restart the system call - no handlers present */
		unsigned int *usp = (unsigned int *)regs->gr[30];

		/* Setup a trampoline to restart the syscall
		 * with __NR_restart_syscall
		 *
		 *  0: <return address (orig r31)>
		 *  4: <2nd half for 64-bit>
		 *  8: ldw 0(%sp), %r31
		 * 12: be 0x100(%sr2, %r0)
		 * 16: ldi __NR_restart_syscall, %r20
		 */
#ifdef CONFIG_64BIT
		put_user(regs->gr[31] >> 32, &usp[0]);
		put_user(regs->gr[31] & 0xffffffff, &usp[1]);
		put_user(0x0fc010df, &usp[2]);
#else
		put_user(regs->gr[31], &usp[0]);
		put_user(0x0fc0109f, &usp[2]);
#endif
		put_user(0xe0008200, &usp[3]);
		put_user(0x34140000, &usp[4]);

		/* Stack is 64-byte aligned, and we only need
		 * to flush 1 cache line.
		 * Flushing one cacheline is cheap.
		 * "sync" on bigger (> 4 way) boxes is not.
		 */
		flush_icache_range(regs->gr[30], regs->gr[30] + 4);

		regs->gr[31] = regs->gr[30] + 8;
		/* Preserve original r28. */
		regs->gr[28] = regs->orig_r28;

		return;
	}
	case -ERESTARTNOHAND:
	case -ERESTARTSYS:
	case -ERESTARTNOINTR: {
		/* Hooray for delayed branching.  We don't
		 * have to restore %r20 (the system call
		 * number) because it gets loaded in the delay
		 * slot of the branch external instruction.
		 */
		regs->gr[31] -= 8;
		/* Preserve original r28. */
		regs->gr[28] = regs->orig_r28;

		return;
	}
	default:
		break;
	}
}

/*
 * Note that 'init' is a special process: it doesn't get signals it doesn't
 * want to handle. Thus you cannot kill init even with a SIGKILL even by
@@ -482,7 +573,6 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
 * registers).  As noted below, the syscall number gets restored for
 * us due to the magic of delayed branching.
 */

asmlinkage void
do_signal(struct pt_regs *regs, long in_syscall)
{
@@ -518,36 +608,14 @@ do_signal(struct pt_regs *regs, long in_syscall)
		  break;
		
		/* Restart a system call if necessary. */
		if (in_syscall) {
			/* Check the return code */
			switch (regs->gr[28]) {
		        case -ERESTART_RESTARTBLOCK:
				current_thread_info()->restart_block.fn = do_no_restart_syscall;
			case -ERESTARTNOHAND:
				DBG(1,"ERESTARTNOHAND: returning -EINTR\n");
				regs->gr[28] = -EINTR;
				break;
		if (in_syscall)
			syscall_restart(regs, &ka);

			case -ERESTARTSYS:
				if (!(ka.sa.sa_flags & SA_RESTART)) {
					DBG(1,"ERESTARTSYS: putting -EINTR\n");
					regs->gr[28] = -EINTR;
					break;
				}
			/* fallthrough */
			case -ERESTARTNOINTR:
				/* A syscall is just a branch, so all
				   we have to do is fiddle the return pointer. */
				regs->gr[31] -= 8; /* delayed branching */
				/* Preserve original r28. */
				regs->gr[28] = regs->orig_r28;
				break;
			}
		}
		/* Whee!  Actually deliver the signal.  If the
		   delivery failed, we need to continue to iterate in
		   this loop so we can deliver the SIGSEGV... */
		if (handle_signal(signr, &info, &ka, oldset, regs, in_syscall)) {
		if (handle_signal(signr, &info, &ka, oldset,
				  regs, in_syscall)) {
			DBG(1,KERN_DEBUG "do_signal: Exit (success), regs->gr[28] = %ld\n",
				regs->gr[28]);
			if (test_thread_flag(TIF_RESTORE_SIGMASK))
@@ -558,57 +626,8 @@ do_signal(struct pt_regs *regs, long in_syscall)
	/* end of while(1) looping forever if we can't force a signal */

	/* Did we come from a system call? */
	if (in_syscall) {
		/* Restart the system call - no handlers present */
		if (regs->gr[28] == -ERESTART_RESTARTBLOCK) {
			unsigned int *usp = (unsigned int *)regs->gr[30];

			/* Setup a trampoline to restart the syscall
			 * with __NR_restart_syscall
			 *
			 *  0: <return address (orig r31)>
			 *  4: <2nd half for 64-bit>
			 *  8: ldw 0(%sp), %r31
			 * 12: be 0x100(%sr2, %r0)
			 * 16: ldi __NR_restart_syscall, %r20
			 */
#ifndef __LP64__
			put_user(regs->gr[31], &usp[0]);
			put_user(0x0fc0109f, &usp[2]);
#else
			put_user(regs->gr[31] >> 32, &usp[0]);
			put_user(regs->gr[31] & 0xffffffff, &usp[1]);
			put_user(0x0fc010df, &usp[2]);
#endif
			put_user(0xe0008200, &usp[3]);
			put_user(0x34140000, &usp[4]);

			/* Stack is 64-byte aligned, and we only need
			 * to flush 1 cache line.
			 * Flushing one cacheline is cheap.
			 * "sync" on bigger (> 4 way) boxes is not.
			 */
			asm("fdc %%r0(%%sr3, %0)\n"
			    "sync\n"
			    "fic %%r0(%%sr3, %0)\n"
			    "sync\n"
			    : : "r"(regs->gr[30]));

			regs->gr[31] = regs->gr[30] + 8;
			/* Preserve original r28. */
			regs->gr[28] = regs->orig_r28;
		} else if (regs->gr[28] == -ERESTARTNOHAND ||
		           regs->gr[28] == -ERESTARTSYS ||
		           regs->gr[28] == -ERESTARTNOINTR) {
			/* Hooray for delayed branching.  We don't
                           have to restore %r20 (the system call
                           number) because it gets loaded in the delay
                           slot of the branch external instruction. */
			regs->gr[31] -= 8;
			/* Preserve original r28. */
			regs->gr[28] = regs->orig_r28;
		}
	}
	if (in_syscall)
		insert_restart_trampoline(regs);
	
	DBG(1,"do_signal: Exit (not delivered), regs->gr[28] = %ld\n", 
		regs->gr[28]);