|
@@ -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;
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
+ if (in_syscall)
|
|
|
+ syscall_restart(regs, &ka);
|
|
|
+
|
|
|
/* 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]);
|