diff options
Diffstat (limited to 'arch/mips/kernel/signal.c')
-rw-r--r-- | arch/mips/kernel/signal.c | 130 |
1 files changed, 64 insertions, 66 deletions
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 6254041b942f..2099d5a4c4b7 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c | |||
@@ -32,50 +32,33 @@ | |||
32 | #include <asm/ucontext.h> | 32 | #include <asm/ucontext.h> |
33 | #include <asm/cpu-features.h> | 33 | #include <asm/cpu-features.h> |
34 | #include <asm/war.h> | 34 | #include <asm/war.h> |
35 | #include <asm/vdso.h> | ||
35 | 36 | ||
36 | #include "signal-common.h" | 37 | #include "signal-common.h" |
37 | 38 | ||
38 | /* | 39 | static int (*save_fp_context)(struct sigcontext __user *sc); |
39 | * Horribly complicated - with the bloody RM9000 workarounds enabled | 40 | static int (*restore_fp_context)(struct sigcontext __user *sc); |
40 | * the signal trampolines is moving to the end of the structure so we can | 41 | |
41 | * increase the alignment without breaking software compatibility. | 42 | extern asmlinkage int _save_fp_context(struct sigcontext __user *sc); |
42 | */ | 43 | extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc); |
43 | #if ICACHE_REFILLS_WORKAROUND_WAR == 0 | 44 | |
45 | extern asmlinkage int fpu_emulator_save_context(struct sigcontext __user *sc); | ||
46 | extern asmlinkage int fpu_emulator_restore_context(struct sigcontext __user *sc); | ||
44 | 47 | ||
45 | struct sigframe { | 48 | struct sigframe { |
46 | u32 sf_ass[4]; /* argument save space for o32 */ | 49 | u32 sf_ass[4]; /* argument save space for o32 */ |
47 | u32 sf_code[2]; /* signal trampoline */ | 50 | u32 sf_pad[2]; /* Was: signal trampoline */ |
48 | struct sigcontext sf_sc; | 51 | struct sigcontext sf_sc; |
49 | sigset_t sf_mask; | 52 | sigset_t sf_mask; |
50 | }; | 53 | }; |
51 | 54 | ||
52 | struct rt_sigframe { | 55 | struct rt_sigframe { |
53 | u32 rs_ass[4]; /* argument save space for o32 */ | 56 | u32 rs_ass[4]; /* argument save space for o32 */ |
54 | u32 rs_code[2]; /* signal trampoline */ | 57 | u32 rs_pad[2]; /* Was: signal trampoline */ |
55 | struct siginfo rs_info; | ||
56 | struct ucontext rs_uc; | ||
57 | }; | ||
58 | |||
59 | #else | ||
60 | |||
61 | struct sigframe { | ||
62 | u32 sf_ass[4]; /* argument save space for o32 */ | ||
63 | u32 sf_pad[2]; | ||
64 | struct sigcontext sf_sc; /* hw context */ | ||
65 | sigset_t sf_mask; | ||
66 | u32 sf_code[8] ____cacheline_aligned; /* signal trampoline */ | ||
67 | }; | ||
68 | |||
69 | struct rt_sigframe { | ||
70 | u32 rs_ass[4]; /* argument save space for o32 */ | ||
71 | u32 rs_pad[2]; | ||
72 | struct siginfo rs_info; | 58 | struct siginfo rs_info; |
73 | struct ucontext rs_uc; | 59 | struct ucontext rs_uc; |
74 | u32 rs_code[8] ____cacheline_aligned; /* signal trampoline */ | ||
75 | }; | 60 | }; |
76 | 61 | ||
77 | #endif | ||
78 | |||
79 | /* | 62 | /* |
80 | * Helper routines | 63 | * Helper routines |
81 | */ | 64 | */ |
@@ -257,32 +240,6 @@ void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, | |||
257 | return (void __user *)((sp - frame_size) & (ICACHE_REFILLS_WORKAROUND_WAR ? ~(cpu_icache_line_size()-1) : ALMASK)); | 240 | return (void __user *)((sp - frame_size) & (ICACHE_REFILLS_WORKAROUND_WAR ? ~(cpu_icache_line_size()-1) : ALMASK)); |
258 | } | 241 | } |
259 | 242 | ||
260 | int install_sigtramp(unsigned int __user *tramp, unsigned int syscall) | ||
261 | { | ||
262 | int err; | ||
263 | |||
264 | /* | ||
265 | * Set up the return code ... | ||
266 | * | ||
267 | * li v0, __NR__foo_sigreturn | ||
268 | * syscall | ||
269 | */ | ||
270 | |||
271 | err = __put_user(0x24020000 + syscall, tramp + 0); | ||
272 | err |= __put_user(0x0000000c , tramp + 1); | ||
273 | if (ICACHE_REFILLS_WORKAROUND_WAR) { | ||
274 | err |= __put_user(0, tramp + 2); | ||
275 | err |= __put_user(0, tramp + 3); | ||
276 | err |= __put_user(0, tramp + 4); | ||
277 | err |= __put_user(0, tramp + 5); | ||
278 | err |= __put_user(0, tramp + 6); | ||
279 | err |= __put_user(0, tramp + 7); | ||
280 | } | ||
281 | flush_cache_sigtramp((unsigned long) tramp); | ||
282 | |||
283 | return err; | ||
284 | } | ||
285 | |||
286 | /* | 243 | /* |
287 | * Atomically swap in the new signal mask, and wait for a signal. | 244 | * Atomically swap in the new signal mask, and wait for a signal. |
288 | */ | 245 | */ |
@@ -475,8 +432,8 @@ badframe: | |||
475 | } | 432 | } |
476 | 433 | ||
477 | #ifdef CONFIG_TRAD_SIGNALS | 434 | #ifdef CONFIG_TRAD_SIGNALS |
478 | static int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, | 435 | static int setup_frame(void *sig_return, struct k_sigaction *ka, |
479 | int signr, sigset_t *set) | 436 | struct pt_regs *regs, int signr, sigset_t *set) |
480 | { | 437 | { |
481 | struct sigframe __user *frame; | 438 | struct sigframe __user *frame; |
482 | int err = 0; | 439 | int err = 0; |
@@ -485,8 +442,6 @@ static int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
485 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) | 442 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) |
486 | goto give_sigsegv; | 443 | goto give_sigsegv; |
487 | 444 | ||
488 | err |= install_sigtramp(frame->sf_code, __NR_sigreturn); | ||
489 | |||
490 | err |= setup_sigcontext(regs, &frame->sf_sc); | 445 | err |= setup_sigcontext(regs, &frame->sf_sc); |
491 | err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set)); | 446 | err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set)); |
492 | if (err) | 447 | if (err) |
@@ -506,7 +461,7 @@ static int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
506 | regs->regs[ 5] = 0; | 461 | regs->regs[ 5] = 0; |
507 | regs->regs[ 6] = (unsigned long) &frame->sf_sc; | 462 | regs->regs[ 6] = (unsigned long) &frame->sf_sc; |
508 | regs->regs[29] = (unsigned long) frame; | 463 | regs->regs[29] = (unsigned long) frame; |
509 | regs->regs[31] = (unsigned long) frame->sf_code; | 464 | regs->regs[31] = (unsigned long) sig_return; |
510 | regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; | 465 | regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; |
511 | 466 | ||
512 | DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n", | 467 | DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n", |
@@ -520,8 +475,9 @@ give_sigsegv: | |||
520 | } | 475 | } |
521 | #endif | 476 | #endif |
522 | 477 | ||
523 | static int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, | 478 | static int setup_rt_frame(void *sig_return, struct k_sigaction *ka, |
524 | int signr, sigset_t *set, siginfo_t *info) | 479 | struct pt_regs *regs, int signr, sigset_t *set, |
480 | siginfo_t *info) | ||
525 | { | 481 | { |
526 | struct rt_sigframe __user *frame; | 482 | struct rt_sigframe __user *frame; |
527 | int err = 0; | 483 | int err = 0; |
@@ -530,8 +486,6 @@ static int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
530 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) | 486 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) |
531 | goto give_sigsegv; | 487 | goto give_sigsegv; |
532 | 488 | ||
533 | err |= install_sigtramp(frame->rs_code, __NR_rt_sigreturn); | ||
534 | |||
535 | /* Create siginfo. */ | 489 | /* Create siginfo. */ |
536 | err |= copy_siginfo_to_user(&frame->rs_info, info); | 490 | err |= copy_siginfo_to_user(&frame->rs_info, info); |
537 | 491 | ||
@@ -564,7 +518,7 @@ static int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
564 | regs->regs[ 5] = (unsigned long) &frame->rs_info; | 518 | regs->regs[ 5] = (unsigned long) &frame->rs_info; |
565 | regs->regs[ 6] = (unsigned long) &frame->rs_uc; | 519 | regs->regs[ 6] = (unsigned long) &frame->rs_uc; |
566 | regs->regs[29] = (unsigned long) frame; | 520 | regs->regs[29] = (unsigned long) frame; |
567 | regs->regs[31] = (unsigned long) frame->rs_code; | 521 | regs->regs[31] = (unsigned long) sig_return; |
568 | regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; | 522 | regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; |
569 | 523 | ||
570 | DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n", | 524 | DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n", |
@@ -581,8 +535,11 @@ give_sigsegv: | |||
581 | struct mips_abi mips_abi = { | 535 | struct mips_abi mips_abi = { |
582 | #ifdef CONFIG_TRAD_SIGNALS | 536 | #ifdef CONFIG_TRAD_SIGNALS |
583 | .setup_frame = setup_frame, | 537 | .setup_frame = setup_frame, |
538 | .signal_return_offset = offsetof(struct mips_vdso, signal_trampoline), | ||
584 | #endif | 539 | #endif |
585 | .setup_rt_frame = setup_rt_frame, | 540 | .setup_rt_frame = setup_rt_frame, |
541 | .rt_signal_return_offset = | ||
542 | offsetof(struct mips_vdso, rt_signal_trampoline), | ||
586 | .restart = __NR_restart_syscall | 543 | .restart = __NR_restart_syscall |
587 | }; | 544 | }; |
588 | 545 | ||
@@ -590,6 +547,8 @@ static int handle_signal(unsigned long sig, siginfo_t *info, | |||
590 | struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) | 547 | struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) |
591 | { | 548 | { |
592 | int ret; | 549 | int ret; |
550 | struct mips_abi *abi = current->thread.abi; | ||
551 | void *vdso = current->mm->context.vdso; | ||
593 | 552 | ||
594 | switch(regs->regs[0]) { | 553 | switch(regs->regs[0]) { |
595 | case ERESTART_RESTARTBLOCK: | 554 | case ERESTART_RESTARTBLOCK: |
@@ -610,9 +569,11 @@ static int handle_signal(unsigned long sig, siginfo_t *info, | |||
610 | regs->regs[0] = 0; /* Don't deal with this again. */ | 569 | regs->regs[0] = 0; /* Don't deal with this again. */ |
611 | 570 | ||
612 | if (sig_uses_siginfo(ka)) | 571 | if (sig_uses_siginfo(ka)) |
613 | ret = current->thread.abi->setup_rt_frame(ka, regs, sig, oldset, info); | 572 | ret = abi->setup_rt_frame(vdso + abi->rt_signal_return_offset, |
573 | ka, regs, sig, oldset, info); | ||
614 | else | 574 | else |
615 | ret = current->thread.abi->setup_frame(ka, regs, sig, oldset); | 575 | ret = abi->setup_frame(vdso + abi->signal_return_offset, |
576 | ka, regs, sig, oldset); | ||
616 | 577 | ||
617 | spin_lock_irq(¤t->sighand->siglock); | 578 | spin_lock_irq(¤t->sighand->siglock); |
618 | sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); | 579 | sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); |
@@ -709,3 +670,40 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused, | |||
709 | key_replace_session_keyring(); | 670 | key_replace_session_keyring(); |
710 | } | 671 | } |
711 | } | 672 | } |
673 | |||
674 | #ifdef CONFIG_SMP | ||
675 | static int smp_save_fp_context(struct sigcontext __user *sc) | ||
676 | { | ||
677 | return raw_cpu_has_fpu | ||
678 | ? _save_fp_context(sc) | ||
679 | : fpu_emulator_save_context(sc); | ||
680 | } | ||
681 | |||
682 | static int smp_restore_fp_context(struct sigcontext __user *sc) | ||
683 | { | ||
684 | return raw_cpu_has_fpu | ||
685 | ? _restore_fp_context(sc) | ||
686 | : fpu_emulator_restore_context(sc); | ||
687 | } | ||
688 | #endif | ||
689 | |||
690 | static int signal_setup(void) | ||
691 | { | ||
692 | #ifdef CONFIG_SMP | ||
693 | /* For now just do the cpu_has_fpu check when the functions are invoked */ | ||
694 | save_fp_context = smp_save_fp_context; | ||
695 | restore_fp_context = smp_restore_fp_context; | ||
696 | #else | ||
697 | if (cpu_has_fpu) { | ||
698 | save_fp_context = _save_fp_context; | ||
699 | restore_fp_context = _restore_fp_context; | ||
700 | } else { | ||
701 | save_fp_context = fpu_emulator_save_context; | ||
702 | restore_fp_context = fpu_emulator_restore_context; | ||
703 | } | ||
704 | #endif | ||
705 | |||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | arch_initcall(signal_setup); | ||