diff options
Diffstat (limited to 'arch/parisc/kernel/signal.c')
-rw-r--r-- | arch/parisc/kernel/signal.c | 273 |
1 files changed, 132 insertions, 141 deletions
diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c index ee6653edeb7a..9784e405f849 100644 --- a/arch/parisc/kernel/signal.c +++ b/arch/parisc/kernel/signal.c | |||
@@ -59,58 +59,13 @@ | |||
59 | * this. */ | 59 | * this. */ |
60 | #define A(__x) ((unsigned long)(__x)) | 60 | #define A(__x) ((unsigned long)(__x)) |
61 | 61 | ||
62 | int do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall); | ||
63 | |||
64 | /* | 62 | /* |
65 | * Atomically swap in the new signal mask, and wait for a signal. | 63 | * Atomically swap in the new signal mask, and wait for a signal. |
66 | */ | 64 | */ |
67 | #ifdef __LP64__ | 65 | #ifdef CONFIG_64BIT |
68 | #include "sys32.h" | 66 | #include "sys32.h" |
69 | #endif | 67 | #endif |
70 | 68 | ||
71 | asmlinkage int | ||
72 | sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, struct pt_regs *regs) | ||
73 | { | ||
74 | sigset_t saveset, newset; | ||
75 | #ifdef __LP64__ | ||
76 | compat_sigset_t newset32; | ||
77 | |||
78 | if (is_compat_task()) { | ||
79 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
80 | if (sigsetsize != sizeof(compat_sigset_t)) | ||
81 | return -EINVAL; | ||
82 | if (copy_from_user(&newset32, (compat_sigset_t __user *)unewset, sizeof(newset32))) | ||
83 | return -EFAULT; | ||
84 | sigset_32to64(&newset,&newset32); | ||
85 | |||
86 | } else | ||
87 | #endif | ||
88 | { | ||
89 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
90 | if (sigsetsize != sizeof(sigset_t)) | ||
91 | return -EINVAL; | ||
92 | |||
93 | if (copy_from_user(&newset, unewset, sizeof(newset))) | ||
94 | return -EFAULT; | ||
95 | } | ||
96 | |||
97 | sigdelsetmask(&newset, ~_BLOCKABLE); | ||
98 | |||
99 | spin_lock_irq(¤t->sighand->siglock); | ||
100 | saveset = current->blocked; | ||
101 | current->blocked = newset; | ||
102 | recalc_sigpending(); | ||
103 | spin_unlock_irq(¤t->sighand->siglock); | ||
104 | |||
105 | regs->gr[28] = -EINTR; | ||
106 | while (1) { | ||
107 | current->state = TASK_INTERRUPTIBLE; | ||
108 | schedule(); | ||
109 | if (do_signal(&saveset, regs, 1)) | ||
110 | return -EINTR; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | /* | 69 | /* |
115 | * Do a signal return - restore sigcontext. | 70 | * Do a signal return - restore sigcontext. |
116 | */ | 71 | */ |
@@ -148,7 +103,7 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall) | |||
148 | sigset_t set; | 103 | sigset_t set; |
149 | unsigned long usp = (regs->gr[30] & ~(0x01UL)); | 104 | unsigned long usp = (regs->gr[30] & ~(0x01UL)); |
150 | unsigned long sigframe_size = PARISC_RT_SIGFRAME_SIZE; | 105 | unsigned long sigframe_size = PARISC_RT_SIGFRAME_SIZE; |
151 | #ifdef __LP64__ | 106 | #ifdef CONFIG_64BIT |
152 | compat_sigset_t compat_set; | 107 | compat_sigset_t compat_set; |
153 | struct compat_rt_sigframe __user * compat_frame; | 108 | struct compat_rt_sigframe __user * compat_frame; |
154 | 109 | ||
@@ -162,7 +117,7 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall) | |||
162 | (usp - sigframe_size); | 117 | (usp - sigframe_size); |
163 | DBG(2,"sys_rt_sigreturn: frame is %p\n", frame); | 118 | DBG(2,"sys_rt_sigreturn: frame is %p\n", frame); |
164 | 119 | ||
165 | #ifdef __LP64__ | 120 | #ifdef CONFIG_64BIT |
166 | compat_frame = (struct compat_rt_sigframe __user *)frame; | 121 | compat_frame = (struct compat_rt_sigframe __user *)frame; |
167 | 122 | ||
168 | if (is_compat_task()) { | 123 | if (is_compat_task()) { |
@@ -184,7 +139,7 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall) | |||
184 | spin_unlock_irq(¤t->sighand->siglock); | 139 | spin_unlock_irq(¤t->sighand->siglock); |
185 | 140 | ||
186 | /* Good thing we saved the old gr[30], eh? */ | 141 | /* Good thing we saved the old gr[30], eh? */ |
187 | #ifdef __LP64__ | 142 | #ifdef CONFIG_64BIT |
188 | if (is_compat_task()) { | 143 | if (is_compat_task()) { |
189 | DBG(1,"sys_rt_sigreturn: compat_frame->uc.uc_mcontext 0x%p\n", | 144 | DBG(1,"sys_rt_sigreturn: compat_frame->uc.uc_mcontext 0x%p\n", |
190 | &compat_frame->uc.uc_mcontext); | 145 | &compat_frame->uc.uc_mcontext); |
@@ -296,7 +251,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
296 | unsigned long rp, usp; | 251 | unsigned long rp, usp; |
297 | unsigned long haddr, sigframe_size; | 252 | unsigned long haddr, sigframe_size; |
298 | int err = 0; | 253 | int err = 0; |
299 | #ifdef __LP64__ | 254 | #ifdef CONFIG_64BIT |
300 | compat_int_t compat_val; | 255 | compat_int_t compat_val; |
301 | struct compat_rt_sigframe __user * compat_frame; | 256 | struct compat_rt_sigframe __user * compat_frame; |
302 | compat_sigset_t compat_set; | 257 | compat_sigset_t compat_set; |
@@ -310,7 +265,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
310 | DBG(1,"setup_rt_frame: frame %p info %p\n", frame, info); | 265 | DBG(1,"setup_rt_frame: frame %p info %p\n", frame, info); |
311 | 266 | ||
312 | 267 | ||
313 | #ifdef __LP64__ | 268 | #ifdef CONFIG_64BIT |
314 | 269 | ||
315 | compat_frame = (struct compat_rt_sigframe __user *)frame; | 270 | compat_frame = (struct compat_rt_sigframe __user *)frame; |
316 | 271 | ||
@@ -390,7 +345,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
390 | 345 | ||
391 | haddr = A(ka->sa.sa_handler); | 346 | haddr = A(ka->sa.sa_handler); |
392 | /* The sa_handler may be a pointer to a function descriptor */ | 347 | /* The sa_handler may be a pointer to a function descriptor */ |
393 | #ifdef __LP64__ | 348 | #ifdef CONFIG_64BIT |
394 | if (is_compat_task()) { | 349 | if (is_compat_task()) { |
395 | #endif | 350 | #endif |
396 | if (haddr & PA_PLABEL_FDESC) { | 351 | if (haddr & PA_PLABEL_FDESC) { |
@@ -405,7 +360,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
405 | haddr = fdesc.addr; | 360 | haddr = fdesc.addr; |
406 | regs->gr[19] = fdesc.gp; | 361 | regs->gr[19] = fdesc.gp; |
407 | } | 362 | } |
408 | #ifdef __LP64__ | 363 | #ifdef CONFIG_64BIT |
409 | } else { | 364 | } else { |
410 | Elf64_Fdesc fdesc; | 365 | Elf64_Fdesc fdesc; |
411 | Elf64_Fdesc __user *ufdesc = (Elf64_Fdesc __user *)A(haddr & ~3); | 366 | Elf64_Fdesc __user *ufdesc = (Elf64_Fdesc __user *)A(haddr & ~3); |
@@ -425,19 +380,19 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
425 | /* The syscall return path will create IAOQ values from r31. | 380 | /* The syscall return path will create IAOQ values from r31. |
426 | */ | 381 | */ |
427 | sigframe_size = PARISC_RT_SIGFRAME_SIZE; | 382 | sigframe_size = PARISC_RT_SIGFRAME_SIZE; |
428 | #ifdef __LP64__ | 383 | #ifdef CONFIG_64BIT |
429 | if (is_compat_task()) | 384 | if (is_compat_task()) |
430 | sigframe_size = PARISC_RT_SIGFRAME_SIZE32; | 385 | sigframe_size = PARISC_RT_SIGFRAME_SIZE32; |
431 | #endif | 386 | #endif |
432 | if (in_syscall) { | 387 | if (in_syscall) { |
433 | regs->gr[31] = haddr; | 388 | regs->gr[31] = haddr; |
434 | #ifdef __LP64__ | 389 | #ifdef CONFIG_64BIT |
435 | if (!test_thread_flag(TIF_32BIT)) | 390 | if (!test_thread_flag(TIF_32BIT)) |
436 | sigframe_size |= 1; | 391 | sigframe_size |= 1; |
437 | #endif | 392 | #endif |
438 | } else { | 393 | } else { |
439 | unsigned long psw = USER_PSW; | 394 | unsigned long psw = USER_PSW; |
440 | #ifdef __LP64__ | 395 | #ifdef CONFIG_64BIT |
441 | if (!test_thread_flag(TIF_32BIT)) | 396 | if (!test_thread_flag(TIF_32BIT)) |
442 | psw |= PSW_W; | 397 | psw |= PSW_W; |
443 | #endif | 398 | #endif |
@@ -462,7 +417,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
462 | regs->gr[2] = rp; /* userland return pointer */ | 417 | regs->gr[2] = rp; /* userland return pointer */ |
463 | regs->gr[26] = sig; /* signal number */ | 418 | regs->gr[26] = sig; /* signal number */ |
464 | 419 | ||
465 | #ifdef __LP64__ | 420 | #ifdef CONFIG_64BIT |
466 | if (is_compat_task()) { | 421 | if (is_compat_task()) { |
467 | regs->gr[25] = A(&compat_frame->info); /* siginfo pointer */ | 422 | regs->gr[25] = A(&compat_frame->info); /* siginfo pointer */ |
468 | regs->gr[24] = A(&compat_frame->uc); /* ucontext pointer */ | 423 | regs->gr[24] = A(&compat_frame->uc); /* ucontext pointer */ |
@@ -516,6 +471,97 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, | |||
516 | return 1; | 471 | return 1; |
517 | } | 472 | } |
518 | 473 | ||
474 | static inline void | ||
475 | syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) | ||
476 | { | ||
477 | /* Check the return code */ | ||
478 | switch (regs->gr[28]) { | ||
479 | case -ERESTART_RESTARTBLOCK: | ||
480 | current_thread_info()->restart_block.fn = | ||
481 | do_no_restart_syscall; | ||
482 | case -ERESTARTNOHAND: | ||
483 | DBG(1,"ERESTARTNOHAND: returning -EINTR\n"); | ||
484 | regs->gr[28] = -EINTR; | ||
485 | break; | ||
486 | |||
487 | case -ERESTARTSYS: | ||
488 | if (!(ka->sa.sa_flags & SA_RESTART)) { | ||
489 | DBG(1,"ERESTARTSYS: putting -EINTR\n"); | ||
490 | regs->gr[28] = -EINTR; | ||
491 | break; | ||
492 | } | ||
493 | /* fallthrough */ | ||
494 | case -ERESTARTNOINTR: | ||
495 | /* A syscall is just a branch, so all | ||
496 | * we have to do is fiddle the return pointer. | ||
497 | */ | ||
498 | regs->gr[31] -= 8; /* delayed branching */ | ||
499 | /* Preserve original r28. */ | ||
500 | regs->gr[28] = regs->orig_r28; | ||
501 | break; | ||
502 | } | ||
503 | } | ||
504 | |||
505 | static inline void | ||
506 | insert_restart_trampoline(struct pt_regs *regs) | ||
507 | { | ||
508 | switch(regs->gr[28]) { | ||
509 | case -ERESTART_RESTARTBLOCK: { | ||
510 | /* Restart the system call - no handlers present */ | ||
511 | unsigned int *usp = (unsigned int *)regs->gr[30]; | ||
512 | |||
513 | /* Setup a trampoline to restart the syscall | ||
514 | * with __NR_restart_syscall | ||
515 | * | ||
516 | * 0: <return address (orig r31)> | ||
517 | * 4: <2nd half for 64-bit> | ||
518 | * 8: ldw 0(%sp), %r31 | ||
519 | * 12: be 0x100(%sr2, %r0) | ||
520 | * 16: ldi __NR_restart_syscall, %r20 | ||
521 | */ | ||
522 | #ifdef CONFIG_64BIT | ||
523 | put_user(regs->gr[31] >> 32, &usp[0]); | ||
524 | put_user(regs->gr[31] & 0xffffffff, &usp[1]); | ||
525 | put_user(0x0fc010df, &usp[2]); | ||
526 | #else | ||
527 | put_user(regs->gr[31], &usp[0]); | ||
528 | put_user(0x0fc0109f, &usp[2]); | ||
529 | #endif | ||
530 | put_user(0xe0008200, &usp[3]); | ||
531 | put_user(0x34140000, &usp[4]); | ||
532 | |||
533 | /* Stack is 64-byte aligned, and we only need | ||
534 | * to flush 1 cache line. | ||
535 | * Flushing one cacheline is cheap. | ||
536 | * "sync" on bigger (> 4 way) boxes is not. | ||
537 | */ | ||
538 | flush_icache_range(regs->gr[30], regs->gr[30] + 4); | ||
539 | |||
540 | regs->gr[31] = regs->gr[30] + 8; | ||
541 | /* Preserve original r28. */ | ||
542 | regs->gr[28] = regs->orig_r28; | ||
543 | |||
544 | return; | ||
545 | } | ||
546 | case -ERESTARTNOHAND: | ||
547 | case -ERESTARTSYS: | ||
548 | case -ERESTARTNOINTR: { | ||
549 | /* Hooray for delayed branching. We don't | ||
550 | * have to restore %r20 (the system call | ||
551 | * number) because it gets loaded in the delay | ||
552 | * slot of the branch external instruction. | ||
553 | */ | ||
554 | regs->gr[31] -= 8; | ||
555 | /* Preserve original r28. */ | ||
556 | regs->gr[28] = regs->orig_r28; | ||
557 | |||
558 | return; | ||
559 | } | ||
560 | default: | ||
561 | break; | ||
562 | } | ||
563 | } | ||
564 | |||
519 | /* | 565 | /* |
520 | * Note that 'init' is a special process: it doesn't get signals it doesn't | 566 | * Note that 'init' is a special process: it doesn't get signals it doesn't |
521 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | 567 | * want to handle. Thus you cannot kill init even with a SIGKILL even by |
@@ -527,13 +573,13 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, | |||
527 | * registers). As noted below, the syscall number gets restored for | 573 | * registers). As noted below, the syscall number gets restored for |
528 | * us due to the magic of delayed branching. | 574 | * us due to the magic of delayed branching. |
529 | */ | 575 | */ |
530 | 576 | asmlinkage void | |
531 | asmlinkage int | 577 | do_signal(struct pt_regs *regs, long in_syscall) |
532 | do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall) | ||
533 | { | 578 | { |
534 | siginfo_t info; | 579 | siginfo_t info; |
535 | struct k_sigaction ka; | 580 | struct k_sigaction ka; |
536 | int signr; | 581 | int signr; |
582 | sigset_t *oldset; | ||
537 | 583 | ||
538 | DBG(1,"\ndo_signal: oldset=0x%p, regs=0x%p, sr7 %#lx, in_syscall=%d\n", | 584 | DBG(1,"\ndo_signal: oldset=0x%p, regs=0x%p, sr7 %#lx, in_syscall=%d\n", |
539 | oldset, regs, regs->sr[7], in_syscall); | 585 | oldset, regs, regs->sr[7], in_syscall); |
@@ -543,7 +589,9 @@ do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall) | |||
543 | we would be called in that case, but for some reason we | 589 | we would be called in that case, but for some reason we |
544 | are. */ | 590 | are. */ |
545 | 591 | ||
546 | if (!oldset) | 592 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
593 | oldset = ¤t->saved_sigmask; | ||
594 | else | ||
547 | oldset = ¤t->blocked; | 595 | oldset = ¤t->blocked; |
548 | 596 | ||
549 | DBG(1,"do_signal: oldset %08lx / %08lx\n", | 597 | DBG(1,"do_signal: oldset %08lx / %08lx\n", |
@@ -560,98 +608,41 @@ do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall) | |||
560 | break; | 608 | break; |
561 | 609 | ||
562 | /* Restart a system call if necessary. */ | 610 | /* Restart a system call if necessary. */ |
563 | if (in_syscall) { | 611 | if (in_syscall) |
564 | /* Check the return code */ | 612 | syscall_restart(regs, &ka); |
565 | switch (regs->gr[28]) { | 613 | |
566 | case -ERESTART_RESTARTBLOCK: | ||
567 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
568 | case -ERESTARTNOHAND: | ||
569 | DBG(1,"ERESTARTNOHAND: returning -EINTR\n"); | ||
570 | regs->gr[28] = -EINTR; | ||
571 | break; | ||
572 | |||
573 | case -ERESTARTSYS: | ||
574 | if (!(ka.sa.sa_flags & SA_RESTART)) { | ||
575 | DBG(1,"ERESTARTSYS: putting -EINTR\n"); | ||
576 | regs->gr[28] = -EINTR; | ||
577 | break; | ||
578 | } | ||
579 | /* fallthrough */ | ||
580 | case -ERESTARTNOINTR: | ||
581 | /* A syscall is just a branch, so all | ||
582 | we have to do is fiddle the return pointer. */ | ||
583 | regs->gr[31] -= 8; /* delayed branching */ | ||
584 | /* Preserve original r28. */ | ||
585 | regs->gr[28] = regs->orig_r28; | ||
586 | break; | ||
587 | } | ||
588 | } | ||
589 | /* Whee! Actually deliver the signal. If the | 614 | /* Whee! Actually deliver the signal. If the |
590 | delivery failed, we need to continue to iterate in | 615 | delivery failed, we need to continue to iterate in |
591 | this loop so we can deliver the SIGSEGV... */ | 616 | this loop so we can deliver the SIGSEGV... */ |
592 | if (handle_signal(signr, &info, &ka, oldset, regs, in_syscall)) { | 617 | if (handle_signal(signr, &info, &ka, oldset, |
618 | regs, in_syscall)) { | ||
593 | DBG(1,KERN_DEBUG "do_signal: Exit (success), regs->gr[28] = %ld\n", | 619 | DBG(1,KERN_DEBUG "do_signal: Exit (success), regs->gr[28] = %ld\n", |
594 | regs->gr[28]); | 620 | regs->gr[28]); |
595 | return 1; | 621 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
622 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
623 | return; | ||
596 | } | 624 | } |
597 | } | 625 | } |
598 | /* end of while(1) looping forever if we can't force a signal */ | 626 | /* end of while(1) looping forever if we can't force a signal */ |
599 | 627 | ||
600 | /* Did we come from a system call? */ | 628 | /* Did we come from a system call? */ |
601 | if (in_syscall) { | 629 | if (in_syscall) |
602 | /* Restart the system call - no handlers present */ | 630 | insert_restart_trampoline(regs); |
603 | if (regs->gr[28] == -ERESTART_RESTARTBLOCK) { | ||
604 | unsigned int *usp = (unsigned int *)regs->gr[30]; | ||
605 | |||
606 | /* Setup a trampoline to restart the syscall | ||
607 | * with __NR_restart_syscall | ||
608 | * | ||
609 | * 0: <return address (orig r31)> | ||
610 | * 4: <2nd half for 64-bit> | ||
611 | * 8: ldw 0(%sp), %r31 | ||
612 | * 12: be 0x100(%sr2, %r0) | ||
613 | * 16: ldi __NR_restart_syscall, %r20 | ||
614 | */ | ||
615 | #ifndef __LP64__ | ||
616 | put_user(regs->gr[31], &usp[0]); | ||
617 | put_user(0x0fc0109f, &usp[2]); | ||
618 | #else | ||
619 | put_user(regs->gr[31] >> 32, &usp[0]); | ||
620 | put_user(regs->gr[31] & 0xffffffff, &usp[1]); | ||
621 | put_user(0x0fc010df, &usp[2]); | ||
622 | #endif | ||
623 | put_user(0xe0008200, &usp[3]); | ||
624 | put_user(0x34140000, &usp[4]); | ||
625 | |||
626 | /* Stack is 64-byte aligned, and we only need | ||
627 | * to flush 1 cache line. | ||
628 | * Flushing one cacheline is cheap. | ||
629 | * "sync" on bigger (> 4 way) boxes is not. | ||
630 | */ | ||
631 | asm("fdc %%r0(%%sr3, %0)\n" | ||
632 | "sync\n" | ||
633 | "fic %%r0(%%sr3, %0)\n" | ||
634 | "sync\n" | ||
635 | : : "r"(regs->gr[30])); | ||
636 | |||
637 | regs->gr[31] = regs->gr[30] + 8; | ||
638 | /* Preserve original r28. */ | ||
639 | regs->gr[28] = regs->orig_r28; | ||
640 | } else if (regs->gr[28] == -ERESTARTNOHAND || | ||
641 | regs->gr[28] == -ERESTARTSYS || | ||
642 | regs->gr[28] == -ERESTARTNOINTR) { | ||
643 | /* Hooray for delayed branching. We don't | ||
644 | have to restore %r20 (the system call | ||
645 | number) because it gets loaded in the delay | ||
646 | slot of the branch external instruction. */ | ||
647 | regs->gr[31] -= 8; | ||
648 | /* Preserve original r28. */ | ||
649 | regs->gr[28] = regs->orig_r28; | ||
650 | } | ||
651 | } | ||
652 | 631 | ||
653 | DBG(1,"do_signal: Exit (not delivered), regs->gr[28] = %ld\n", | 632 | DBG(1,"do_signal: Exit (not delivered), regs->gr[28] = %ld\n", |
654 | regs->gr[28]); | 633 | regs->gr[28]); |
655 | 634 | ||
656 | return 0; | 635 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { |
636 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
637 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
638 | } | ||
639 | |||
640 | return; | ||
641 | } | ||
642 | |||
643 | void do_notify_resume(struct pt_regs *regs, long in_syscall) | ||
644 | { | ||
645 | if (test_thread_flag(TIF_SIGPENDING) || | ||
646 | test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
647 | do_signal(regs, in_syscall); | ||
657 | } | 648 | } |