diff options
-rw-r--r-- | arch/parisc/kernel/signal.c | 177 |
1 files changed, 98 insertions, 79 deletions
diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c index 8d781b0e668b..64169ab82de2 100644 --- a/arch/parisc/kernel/signal.c +++ b/arch/parisc/kernel/signal.c | |||
@@ -471,6 +471,97 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, | |||
471 | return 1; | 471 | return 1; |
472 | } | 472 | } |
473 | 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 | |||
474 | /* | 565 | /* |
475 | * 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 |
476 | * 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 |
@@ -482,7 +573,6 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, | |||
482 | * registers). As noted below, the syscall number gets restored for | 573 | * registers). As noted below, the syscall number gets restored for |
483 | * us due to the magic of delayed branching. | 574 | * us due to the magic of delayed branching. |
484 | */ | 575 | */ |
485 | |||
486 | asmlinkage void | 576 | asmlinkage void |
487 | do_signal(struct pt_regs *regs, long in_syscall) | 577 | do_signal(struct pt_regs *regs, long in_syscall) |
488 | { | 578 | { |
@@ -518,36 +608,14 @@ do_signal(struct pt_regs *regs, long in_syscall) | |||
518 | break; | 608 | break; |
519 | 609 | ||
520 | /* Restart a system call if necessary. */ | 610 | /* Restart a system call if necessary. */ |
521 | if (in_syscall) { | 611 | if (in_syscall) |
522 | /* Check the return code */ | 612 | syscall_restart(regs, &ka); |
523 | switch (regs->gr[28]) { | 613 | |
524 | case -ERESTART_RESTARTBLOCK: | ||
525 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
526 | case -ERESTARTNOHAND: | ||
527 | DBG(1,"ERESTARTNOHAND: returning -EINTR\n"); | ||
528 | regs->gr[28] = -EINTR; | ||
529 | break; | ||
530 | |||
531 | case -ERESTARTSYS: | ||
532 | if (!(ka.sa.sa_flags & SA_RESTART)) { | ||
533 | DBG(1,"ERESTARTSYS: putting -EINTR\n"); | ||
534 | regs->gr[28] = -EINTR; | ||
535 | break; | ||
536 | } | ||
537 | /* fallthrough */ | ||
538 | case -ERESTARTNOINTR: | ||
539 | /* A syscall is just a branch, so all | ||
540 | we have to do is fiddle the return pointer. */ | ||
541 | regs->gr[31] -= 8; /* delayed branching */ | ||
542 | /* Preserve original r28. */ | ||
543 | regs->gr[28] = regs->orig_r28; | ||
544 | break; | ||
545 | } | ||
546 | } | ||
547 | /* Whee! Actually deliver the signal. If the | 614 | /* Whee! Actually deliver the signal. If the |
548 | delivery failed, we need to continue to iterate in | 615 | delivery failed, we need to continue to iterate in |
549 | this loop so we can deliver the SIGSEGV... */ | 616 | this loop so we can deliver the SIGSEGV... */ |
550 | if (handle_signal(signr, &info, &ka, oldset, regs, in_syscall)) { | 617 | if (handle_signal(signr, &info, &ka, oldset, |
618 | regs, in_syscall)) { | ||
551 | 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", |
552 | regs->gr[28]); | 620 | regs->gr[28]); |
553 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | 621 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
@@ -558,57 +626,8 @@ do_signal(struct pt_regs *regs, long in_syscall) | |||
558 | /* 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 */ |
559 | 627 | ||
560 | /* Did we come from a system call? */ | 628 | /* Did we come from a system call? */ |
561 | if (in_syscall) { | 629 | if (in_syscall) |
562 | /* Restart the system call - no handlers present */ | 630 | insert_restart_trampoline(regs); |
563 | if (regs->gr[28] == -ERESTART_RESTARTBLOCK) { | ||
564 | unsigned int *usp = (unsigned int *)regs->gr[30]; | ||
565 | |||
566 | /* Setup a trampoline to restart the syscall | ||
567 | * with __NR_restart_syscall | ||
568 | * | ||
569 | * 0: <return address (orig r31)> | ||
570 | * 4: <2nd half for 64-bit> | ||
571 | * 8: ldw 0(%sp), %r31 | ||
572 | * 12: be 0x100(%sr2, %r0) | ||
573 | * 16: ldi __NR_restart_syscall, %r20 | ||
574 | */ | ||
575 | #ifndef __LP64__ | ||
576 | put_user(regs->gr[31], &usp[0]); | ||
577 | put_user(0x0fc0109f, &usp[2]); | ||
578 | #else | ||
579 | put_user(regs->gr[31] >> 32, &usp[0]); | ||
580 | put_user(regs->gr[31] & 0xffffffff, &usp[1]); | ||
581 | put_user(0x0fc010df, &usp[2]); | ||
582 | #endif | ||
583 | put_user(0xe0008200, &usp[3]); | ||
584 | put_user(0x34140000, &usp[4]); | ||
585 | |||
586 | /* Stack is 64-byte aligned, and we only need | ||
587 | * to flush 1 cache line. | ||
588 | * Flushing one cacheline is cheap. | ||
589 | * "sync" on bigger (> 4 way) boxes is not. | ||
590 | */ | ||
591 | asm("fdc %%r0(%%sr3, %0)\n" | ||
592 | "sync\n" | ||
593 | "fic %%r0(%%sr3, %0)\n" | ||
594 | "sync\n" | ||
595 | : : "r"(regs->gr[30])); | ||
596 | |||
597 | regs->gr[31] = regs->gr[30] + 8; | ||
598 | /* Preserve original r28. */ | ||
599 | regs->gr[28] = regs->orig_r28; | ||
600 | } else if (regs->gr[28] == -ERESTARTNOHAND || | ||
601 | regs->gr[28] == -ERESTARTSYS || | ||
602 | regs->gr[28] == -ERESTARTNOINTR) { | ||
603 | /* Hooray for delayed branching. We don't | ||
604 | have to restore %r20 (the system call | ||
605 | number) because it gets loaded in the delay | ||
606 | slot of the branch external instruction. */ | ||
607 | regs->gr[31] -= 8; | ||
608 | /* Preserve original r28. */ | ||
609 | regs->gr[28] = regs->orig_r28; | ||
610 | } | ||
611 | } | ||
612 | 631 | ||
613 | 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", |
614 | regs->gr[28]); | 633 | regs->gr[28]); |